IndicShape.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / Shaping / IndicShape.cs / 1 / IndicShape.cs

                            //---------------------------------------------------------------------- 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation, 2003
// 
//  File:      IndicShape.cs
// 
//  Contents:  Implementation of Indic shaping engine and its factory 
//
//  Created:   06-15-2005 Nick Beal (nbeal) 
//
//-----------------------------------------------------------------------

// #define VALIDATE_CLUSTER_PARAMETERS 

using System; 
using System.Security; 
using System.Security.Permissions;
using System.Diagnostics; 
using System.Collections;
using System.Globalization;
using System.Windows;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using MS.Internal.FontCache; 
using MS.Internal.FontFace; 
using MS.Internal.PresentationCore;
 

namespace MS.Internal.Shaping
{
 
    /// 
    /// IndicCharClass - enumeration of Indic classification 
    ///  
    internal enum IndicCharClass : ushort
    { 
        Halant,
        Vedic,
        VowelSign,
        Matra, 
        Ra,             // all these first character classes have repositioning information in their shape info
        Neutral, 
        NBSP, 
        Consonant,
        NuktaConsonant, 
        Nukta,
        Vowel,
        ZWJ,
        ZWNJ, 
        Reserved,     // Unknown class --
        NumberOfCharClasses,  // This needs to be < 16, or else the flags and re-positioning classes will need to shifted 
 
        CharClassMask = 0xF,    // mask off the positioning info flag (used instead of CharShapeInfo.ShaperClassMask)
        IncludesPositioningInfo = 0x10,         // set if classification table entry includes repositioning info 

        // Note that the bits represented by CharClassMask | IncludesPositioningInfo are the
        // same bits as in CharShapeInfo.ShaperClassMask.  This is necessary as the Indic char
        // classifier expects the CharShapeInfo for each Unicode Indic character to have this 
        // IndicCharCLass information in the ShaperClassMask range.
 
 
    };
 

    internal enum IndicRepositioningClass : ushort
    {
        AtStart, 
        BeforeMain,
        AfterMain,          // Repositions to right behind main consonant 
        BeforeSub,          // Repositions to in front of first sub-base consonant's halant 
        AfterSub,           // Repositions to right behind sub-base consonant
        BeforePost,         // Repositions to in front of first post-base consonant's halant 
        AfterPost,          // Repositions to right behind post-base consonant
        BeforeEnd,          // Repositions to end of cluster
        NumberOfRepositioningClasses,    // there're 7 repositioning class members
 
        RepositioningClassMask = 0x07,  // NumberOfRepositioningClasses must fit within this mask
//        IsMatra     = 0x8, // this bit is set if the character is a matra 
 
        // Note that the bits represented by
        // RepositioningClassMask | IsConsonant | IsMatra are the 
        // same bits as in CharShapeInfo.ShaperClassMask.  This is necessary as the Indic char
        // cluster processor expects the CharShapeInfo for each reordering Indic character to
        // have this IndicRepositioningClass information in the ShaperClassMask range.
 
        // for matra's and reph chars, the entry in the char classification table is a packed entity
        // e{IndicCharClass} + ( e{IndicRepositioningClass} << RepositioningClassShift).  So, this 
        // shift constant for normalizing the repositioning class info when it is stored 
        // in a CharShapeInfo entity.   When the char classification information is saved
        // to the ShaperWorkspace shapeinfo array, the IndicCharClass information is 
        // replaced with the repositioning information and the IsConsonant, IsMatra bits
        // are set as appropriate
        RepositioningClassShift = 5,
    }; 

    internal enum ConsonantState : byte 
    { 
        Start,
        AfterZWJ, 
        AfterReph,
        AfterMain,
        AfterSub,
        AfterPost, 
        AfterDravidianBase,
        EndOfClusterReached 
    } 

 
    enum IndicFormFlags : byte
    {
        InitForm      = 0x01,   // only used in Bengali to flag that this character may take init form
 
        // Consonant flags (These are generally font dependent and will be cached with the font
        // data). 
        PostbaseReph  = 0x02, 
        DravidianBase = 0x04,
        RephForm      = 0x08, 
        VattuForm     = 0x10,
        SubForm       = 0x20,
        PostForm      = 0x40,
        HalfForm      = 0x80, 

        // Matra Flags (these indicate glyph's position relative to the base; These are not font 
        // dependent but will be cached with the consonant form flags). 
        PreBaseMatra   = 0x02,     // matra modifier
        AboveBaseMatra = 0x04,     // matra modifier 
        BelowBaseMatra = 0x08,     // matra modifiers
        PostBaseMatra  = 0x10,     // matra modifier
    };
 

    ///  
    /// The Indic Shaping Engine - (shapes Indic text) 
    /// 
    ///  
    /// The IShaper and IShapingEngine interfaces are implemented to
    /// provide the shaping methods for Indic Scripts.
    /// There are four Indic private types defined/used in this class:
    /// 1.) IndicShapeFSM - this class manages the shape information 
    /// 2.) IndicClusterCop - this class manages the canonical ordering
    /// 3.) IndicFontClient - this class manages the font interface 
    /// 4.) IndicCharClassInfo - contains the char classification tables 
    ///
    internal class IndicShape : BaseShape 
    {

        /// 
        /// All required shaping Features (GSUB) 
        /// 
        private static readonly Feature[]   _indicSubstitutionFeatures    = 
        { 
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.PrebaseSubstitutions,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.AboveBaseSubstitutions,1), 
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.BelowBaseSubstitutions,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.PostbaseSubstitutions,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.HalantForms,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.ContextualAlternates,1), 
        };
 
        ///  
        /// All required shaping Features (GPOS)
        ///  
        private static readonly Feature[]   _indicPositioningFeatures    =
        {
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.Kerning,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.Distances,1), 
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.AboveBaseMarkPositioning,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.BelowBaseMarkPositioning,1), 
        }; 

 

        //
        // Indic Script is only supported scripts
        // 
        private static readonly ScriptTags[] _supportedScripts = new ScriptTags[]
            {   ScriptTags.Bengali, 
                ScriptTags.Devanagari, 
                ScriptTags.Gujarati,
                ScriptTags.Gurmukhi, 
                ScriptTags.Kannada,
                ScriptTags.Malayalam,
                ScriptTags.Oriya,
                ScriptTags.Tamil, 
                ScriptTags.Telugu   };
 
 
        /// 
        /// Constructor for the Indic Open Type Shaping Engine. 
        /// 
        internal IndicShape()
        {
            // Indic diacritics have width 
            _forceDiacriticsToZeroWidth = false;
        } 
 

        //-------------------------------------- 
        //
        //  Internal Methods
        //
        //-------------------------------------- 

#region Internal methods 
        ///  
        /// IndicShape.SupportedScripts -
        ///  IShapingEngine member override 
        /// 
        /// Our supported scripts (9 Indic scripts).
        public override ScriptTags[] SupportedScripts
        { 
            get
            { 
                return _supportedScripts; 
            }
        } 

        /// 
        /// IndicShape.GetCharClassifier - creates
        ///  
        /// 
        ///     This will normally be overridden by derived shapers. It is used in OnLoadFont 
        ///  
        protected override ShaperCharacterClassifier GetCharClassifier(ScriptTags scriptTag, GlyphTypeface fontFace)
        { 
            return new IndicCharClassifier (scriptTag, fontFace);
        }

        internal const CharShapeInfo IndicCharClassMask = (CharShapeInfo)IndicCharClass.CharClassMask; 
        internal const ushort RepositioningInfoShift = (ushort)IndicRepositioningClass.RepositioningClassShift;
        internal const CharShapeInfo ShapeIncludesPositioningInfoFlag = (CharShapeInfo) IndicCharClass.IncludesPositioningInfo; 
 

        private bool _forceSerializedOn = false; 

        /// 
        ///     IndicShape.GetGlyphs - Indic implementation of the GetGlyphs() helper function.
        ///  
        /// shaping currentRun
        /// Text item 
        /// number of glyphs 
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical]
        unsafe protected override int GetGlyphs ( ref ShapingWorkspace currentRun, Item item )
        { 

            ushort charsCount = 0; 
 
            RecordTraceEvent(MS.Utility.EventType.StartEvent, "Indic Init Start");
 
            // initialize the cluster cop/state machine
            IndicCharClassifier indicClassifier = (IndicCharClassifier)currentRun.CharConverter;
            IndicShapeFSM stateMachine = new IndicShapeFSM( indicClassifier, currentRun.HasLeadingJoin );
 
            currentRun.IsForceSerializedClusterOn = _forceSerializedOn;
 
            RecordTraceEvent(MS.Utility.EventType.EndEvent, "IndicShape Init End"); 
            RecordTraceEvent(MS.Utility.EventType.StartEvent, "IndicShape ShapeIndicText Start");
 
            // Shape and initialize the glyph list
            // process the char stream, creating shape info, applying features
            // as necessary
            char nextChar; 
            while ( currentRun.GetNextChar(out nextChar) )
            { 
                ++charsCount; 

                CharShapeInfo nextShape = stateMachine.StepToNextState( nextChar ); 

                // Check for special handling; might be a nukta that needs reordering or a matra
                // that needs decomposing or maybe a Tamil akhand has just been completed
                if ((nextShape & CharShapeInfo.RequiresSpecialHandling) != 0) 
                {
                    // special handling required 
 
                    nextShape ^= CharShapeInfo.RequiresSpecialHandling; // clear the special handling flag
                    IndicCharClass nextCharClass = (IndicCharClass)(nextShape & IndicCharClassMask); 
                    Debug.Assert (nextCharClass < IndicCharClass.NumberOfCharClasses, "invalid Indic char class index");
                    switch (nextCharClass)
                    {
                        case IndicCharClass.Nukta: 
                            // swap the nukta and its preceeding halant
                            ushort prevGlyph = currentRun.PreviousGlyph; // get halant glyph 
                            CharShapeInfo prevShape = currentRun.PreviousShape; 
                            currentRun.PreviousGlyph = indicClassifier.ToGlyph(nextChar);
                            currentRun.SetShapeInfo(currentRun.PreviousCharIx,nextShape); 
                            currentRun.SetGlyphPropertiesUsingGlyph(prevShape, prevGlyph);
                            continue;

                        case IndicCharClass.Matra: 
                            // decompose the matra...
                            char[] matraComponents = indicClassifier.ToDecompositionList(currentRun.CurrentChar); 
                            if (matraComponents != null) 
                            {
                                currentRun.AddGlyphs(matraComponents.Length - 1); // add space for the extra glyphs 

                                currentRun.SetGlyphPropertiesUsingChar(nextShape, matraComponents[0] );
                                currentRun.CurrentShape = CharShapeInfo.NoFlagsSet;
                                for (int i = 1; i < matraComponents.Length; ++i) 
                                {
                                    currentRun.SetGlyphProperties(currentRun.CharConverter.ToGlyph( matraComponents[i] ) ); 
                                } 
                                currentRun.CurrentShape = nextShape;
 
                                continue;
                            }
                            break;
 
                        case IndicCharClass.Halant:
                            // a Tamil akhand? 
                            if (!stateMachine.IsAkhandPending(ref currentRun)) 
                            {
                                // no akhand, so this is end of cluster 
                                stateMachine.ResetStateMachine();
                            }
                            break;
                    } 

                } 
 
                currentRun.SetGlyphPropertiesUsingChar(nextShape, nextChar);
            } 

            // now go through the chars/shapes and apply the features cluster by cluster...
            currentRun.Reset(0,0,charsCount);
            IndicClusterCop clusterCop = new IndicClusterCop( (IndicFontClient)currentRun.FontClient ); 
            ushort charsCount2 = 0;
            while (currentRun.ToNextChar()) 
            { 
                ++charsCount2;
 
                if (!clusterCop.AddCharToCluster(ref currentRun))
                {
                    // an unexpected end of cluster has occurred.  we need
                    // to create one or more new clusters... 
                    ushort currentCharIx = currentRun.CurrentCharIx;
 
                    // find the start of the next cluster 
                    ushort nextClusterStartIx = (ushort)(currentCharIx + 1);
                    while (nextClusterStartIx < currentRun.CharsCount && 
                           (currentRun.GetShapeInfo(nextClusterStartIx) & CharShapeInfo.IsStartOfCluster) == 0)
                    {
                        ++nextClusterStartIx;
                    } 

                    // re-cluster the newly "orphaned" characters from the end of this unexpectedly 
                    // done cluster 
                    int orphanedCharsCount = nextClusterStartIx - currentRun.CurrentCharIx;
 
                    while ( orphanedCharsCount-- > 0 )
                    {
                        stateMachine.ResetStateMachine();
                        CharShapeInfo nextShape = stateMachine.StepToNextState(  currentRun.CurrentChar ); 
                        if ((nextShape & CharShapeInfo.RequiresInsertedBase) == CharShapeInfo.RequiresInsertedBase)
                        { 
                            currentRun.UpdateCurrentGlyphProperties(nextShape); 
                            clusterCop.ApplyClusterFeatures(ref currentRun);
                            for (ushort i = currentRun.CharsCount; --i > currentRun.CurrentCharIx;) 
                            {
                                currentRun.CharMap[i]  += 1;
                            }
                        } 

                        if ( orphanedCharsCount > 0 ) 
                        { 
                            ++charsCount2;
                            currentRun.ToNextChar(); 
                        }

                    }
 
                }
 
            } 

            clusterCop.ApplyClusterFeatures(ref currentRun); 
            RecordTraceEvent(MS.Utility.EventType.EndEvent, "IndicShape ShapeIndicText End");

            return currentRun.GlyphsCount;    // we're done
 

        } 
 
        /// 
        ///    IndicShape.ApplySubstitutionFeatures - default implementation of the GetGlyphs() helper function. 
        /// 
        /// shaping currentRun
        /// Set of gsub features to be applied to the unicode run.
        /// result of applying features 
        /// 
        /// Critical - this method calls unsafe methods. 
        ///  
        [SecurityCritical]
        protected override OpenTypeLayoutResult ApplySubstitutionFeatures( 
                                                        ref ShapingWorkspace currentRun,
                                                        FeatureSet       featureSet  )
        {
            //  Apply the Indic Text Features from currentRun 
            ShaperFontClient fontClient = currentRun.FontClient;
            return fontClient.SubstituteGlyphs( 
                                ref currentRun, 
                               _indicSubstitutionFeatures,
                               _indicSubstitutionFeatures.Length ); 

        }

        ///  
        ///    IndicShape.ApplyPositioningFeatures - generic implementation of the GetGlyphPlacement helper.
        ///     This method goes through a list of glyphs and adds placement information. 
        ///  
        /// the wrapper for glyph advances, offset arrays
        /// metrics for all the positioning features 
        /// Set of gpos features to be applied to the unicode run.
        /// result of applying features
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        protected override OpenTypeLayoutResult ApplyPositioningFeatures( 
                ref PlacementWorkspace   placementInfo,
                ref LayoutMetrics   layoutMetrics, 
                FeatureSet          featureSet )
        {
            ShaperFontClient fontClient = placementInfo.FontClient;
            return fontClient.PositionGlyphs( 
                        ref placementInfo,
                        ref layoutMetrics, 
                        _indicPositioningFeatures,     // In: List of features to apply 
                        _indicPositioningFeatures.Length );
 
        }

        /// 
        /// IndicShape.OnLoadFont - IShapingEngine method override. 
        /// 
        ///  
        ///     This should normally be sufficient for most shapers - the only 
        ///     exceptions are those shapers that want to always return true
        ///  
        /// Script of interest.
        /// Font face being loaded
        /// the font client
        /// True if font supports script. 
        /// 
        /// Critical - This method reads into raw font table bits. 
        /// Safe     - This method doesn't expose any critical data. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public override bool OnLoadFont(
            ScriptTags          scriptTag,
            GlyphTypeface       fontFace,
            out object          shaperFontClient 
            )
        { 
            // create the font client for this font/script 
            IndicCharClassifier indicClassifier = new IndicCharClassifier(scriptTag, fontFace);
            IndicFontClient fontClient = new IndicFontClient( fontFace, 
                                                              indicClassifier );


            // if font is happy with this script, return true and give caller the ShaperFontClient 
            // to use in all method calls to us.
            if (fontClient.ValidateScriptTag(scriptTag)) 
            { 
                shaperFontClient = fontClient;
                return true; 
            }
            else
            {
                shaperFontClient = null; 
                return false;
            } 
        } 

    } 
#endregion


    ///  
    /// Class IndicShapeFSM:
    ///  The Indic state machine 
    ///  
    /// 
    /// This class implements the Indic state machine.  The GetNextShape() routine 
    /// is the most important method, returning the corresponding shape flags for each
    /// unicode char in the text stream.
    /// 
    internal struct IndicShapeFSM 
    {
 
        ushort _shapedCharCount; 

        IndicClusterState _currentState; 
        IndicClusterState _previousState;

        IndicCharClassifier _charConverter;
 
        ushort _clusterSize;
 
        public const ushort ClusterSizeLimit = (ushort)15;     // cluster size limit (big enough for any valid cluster) 
        public const ushort MaximumConsonantOffset = (ushort)10; // better be less than ClusterSizeLimit!
 

        char _previousChar;
        bool _isAkhandPending;
        char[][] _akhands; 

        public IndicShapeFSM (IndicCharClassifier charConverter, bool hasLeadingZWJ) 
        { 

            _previousState = _currentState = IndicClusterState.StartState; 
            if (hasLeadingZWJ)
            {
                _currentState = IndicClusterState.StartLinkedState;
            } 

             _shapedCharCount = 0; 
             _clusterSize = 0; 
             _charConverter = charConverter;
 
             _isAkhandPending = false;
             _previousChar = '\0';
             _akhands = (charConverter.ScriptIx == IndicScriptIndices.Tamil) ?
                    charConverter.TamilAkhands : null; 
        }
 
        internal void ResetStateMachine() 
        {
            _currentState = IndicClusterState.StartState; 
            _clusterSize = 0;
            _isAkhandPending = false;
        }
 
        /// 
        ///   IndicShapeFSM.StepToNextState - process the latest character. 
        ///  
        /// 
        ///   This routine steps the state machine to its next state 
        ///   based on the current state and the next char.
        /// 
        /// state flags for current character appropriate to new state
        internal CharShapeInfo StepToNextState (  char nextChar ) 
        {
 
            // get the cluster state entry from the cluster state table.  The char shape flags from this 
            // table form our starting place for
            CharShapeInfo nextCharShape = _charConverter.ToShapeInfo(nextChar); 
            IndicCharClass nextCharClass = (IndicCharClass)(nextCharShape & IndicShape.IndicCharClassMask);
            Debug.Assert (nextCharClass < IndicCharClass.NumberOfCharClasses, "invalid Indic char class index");
            ushort tableEntry =
                IndicClusterStateTable[(((int)_currentState - 1) << 4) + (int)nextCharClass];   // 16 entries per state. 

            // if the char shape info includes repositioning information, save it in the lower nibble of the 
            // returned char shape info (we'll need this later when we are preparing the cluster for feature 
            // application)...
            _previousState = _currentState;     // save current state. 
            _currentState = (IndicClusterState)(tableEntry & ClusterStateMask);

            // some special things to look for...
            bool isRaConsonant = false; 
            switch (nextCharClass)
            { 
                case IndicCharClass.Ra: 
                    nextCharShape = (CharShapeInfo) nextCharClass;
 
                    // If this is supposedly a reph, verify that it is, in fact, a reph
                    if (_currentState == IndicClusterState.RaState)
                    {
                        if ((_charConverter.ToFormInfo(nextChar) & IndicFormFlags.RephForm) == 0) 
                        {
                            isRaConsonant = true; 
                            _currentState = IndicClusterState.ConsonantState; 
                        }
                    } 

                    break;
                case IndicCharClass.Vedic:
                case IndicCharClass.VowelSign: 
                case IndicCharClass.Matra:
                    if ((nextCharShape & (CharShapeInfo) IndicCharClass.IncludesPositioningInfo) == 0) 
                    { 
                        // matras without positioning info (must be a multi-component matra)
                        // must be specially handled (for adding extra glyphs into glyph 
                        // run)
                        nextCharShape |= CharShapeInfo.RequiresSpecialHandling;
                    }
                    else 
                    {
                         nextCharShape = (CharShapeInfo) nextCharClass; 
                    } 

                    break; 
                case IndicCharClass.Halant:
                    nextCharShape = (CharShapeInfo) nextCharClass;
                    if ( _charConverter.ScriptIx == IndicScriptIndices.Tamil)
                    { 
                        // check for akhands...
                        if (_isAkhandPending) 
                        { 
                            ResetStateMachine(); // akhand + halant, this is end of cluster
                            _previousChar = nextChar; 
                            ++_shapedCharCount;
                            return nextCharShape;
                        }
                        else 
                        {
                            nextCharShape |= CharShapeInfo.RequiresSpecialHandling; 
                        } 
                    }
                    break; 
                case IndicCharClass.Nukta:
                     nextCharShape = (CharShapeInfo) nextCharClass;

                        // this is a nukta following a halant, we'll need to swap 'em. 
                    if (_previousState == IndicClusterState.HalantState ||
                        _previousState == IndicClusterState.RephState) 
                    { 
                        nextCharShape |= CharShapeInfo.RequiresSpecialHandling;
                    } 
                    break;

                default:
                    Debug.Assert((nextCharShape & (CharShapeInfo) IndicCharClass.IncludesPositioningInfo) == 0, 
                                 "This character class should not have positioning info in its char shape!");
                    break; 
            } 

            // whenever cluster size is larger than 10, we're gonna get nervous.  Make sure 
            // that any further characters aren't consonants and make sure that under no
            // circumstances will we allow a cluster size greater than 15.
            if ( (tableEntry & StartOfCluster) == 0)
            { 
                if (++_clusterSize > MaximumConsonantOffset)
                { 
                    bool isClusterSizeOk = true; 
                    switch(nextCharClass)
                    { 
                        case IndicCharClass.Ra:
                        case IndicCharClass.Consonant:
                        case IndicCharClass.NuktaConsonant:
                            isClusterSizeOk = false; 
                            break;
                        default: 
                            if (_clusterSize >= ClusterSizeLimit) 
                            {
                                isClusterSizeOk = false; 
                            }
                            break;
                    }
 
                    if (!isClusterSizeOk)
                    { 
                        // arbitrary limit on cluster size.  If its reached, start a new cluster 
                        ResetStateMachine();
                        tableEntry = 
                            IndicClusterStateTable[(((int)_currentState - 1) << 4) + (int)nextCharClass];   // 16 entries per state.
                        _currentState = isRaConsonant ? IndicClusterState.ConsonantState :
                                                    (IndicClusterState)(tableEntry & ClusterStateMask);
 
                        _clusterSize = 1;
                    } 
                } 
            }
            else 
            {
               _clusterSize = 1;
               _isAkhandPending = false;
            } 

            if ( (tableEntry & ClusterShapeFlagsMask) != 0 ) 
            { 
                nextCharShape |= (CharShapeInfo)(tableEntry & ClusterShapeFlagsMask);
            } 

            _previousChar = nextChar;
            ++_shapedCharCount;
            return nextCharShape; 
        }
 
 
        /// 
        ///   IndicShapeFSM.IsAkhandPending - check if akhand is done. 
        /// 
        /// 
        ///   This routine steps the state machine to its next state
        ///   based on the current state and the next char. 
        /// 
        /// true if preceding consonant is valid start of akhand 
        ///  
        /// Critical - calls critical code
        ///  
        [SecurityCritical]
        internal bool IsAkhandPending(ref ShapingWorkspace currentRun)
        {
            _isAkhandPending = false; 
            if (_clusterSize > 1)
            { 
                ushort currentIx = currentRun.CurrentCharIx; 
                for (int i = 0; i < _akhands.Length; ++i)
                { 
                    ushort nextCharIx = (ushort)(currentIx - 1);
                    char[] akhandCandidate = _akhands[i];
                    if (nextCharIx + akhandCandidate.Length <= currentRun.CharsCount)
                    { 
                        _isAkhandPending = true; // we hope!
                        for (int j = 0; j < akhandCandidate.Length; ++j) 
                        { 
                            if (currentRun.GetChar(nextCharIx) != akhandCandidate[j])
                            { 
                                _isAkhandPending = false;   // nope, not this one!
                                break;
                            }
                            ++nextCharIx; 
                        }
 
                        if (_isAkhandPending) 
                        {
                            // we've just started an akhand sequence! 
                            break;
                        }
                    }
                } 
            }
 
            return _isAkhandPending; 
        }
 
        /// 
        /// IndicClusterState - enumeration of Indic cluster states
        /// ordinal position
        ///  
        private enum IndicClusterState : byte
        { 
            NoChange, 
            StartState,
            StartLinkedState, 
            NBSPState,
            StandaloneState,
            VowelState,
            VowelZWJState, 
            ConsonantState,
            RaState, 
            HalantState, 
            RephState,
            VowelSignState, 
            VedicState,
            SecondVedicState,
            ZWJState,
            ZWJHalantState, 
            MatraState,
            FinalState, 
            NumberOfIndicClusterStates, 

            ClusterStateMask = 0x1f,        // enough bits for all the cluster states, must be 
                                            // <= CharShapeInfo.ShapeClassMask
        };

        private const ushort ClusterStateMask = (ushort) IndicClusterState.ClusterStateMask; 
        private const ushort ClusterShapeFlagsMask = (ushort) CharShapeInfo.ShapeFlagsMask;
 
        // These values are used to populate the state tables.  Each entry is a packed 
        // value with the next state in the lower byte, and the shape flags in the upper
        // byte.  The GetNextShape routine unpacks these entries. 
        // The "ToState" definitions generally mean that the current character is
        // a continuation of the current cluster and that only the state machine's state
        // is changing
        private const ushort InvalidBase  = (ushort)CharShapeInfo.RequiresInsertedBase; 
        private const ushort ZWControlChar = (ushort) CharShapeInfo.IsUnicodeLayoutControl;
        private const ushort StartOfCluster = (ushort) CharShapeInfo.IsStartOfCluster; 
        private const ushort NoChange  = (ushort )IndicClusterState.NoChange; 

        private const ushort ToStartState  = (ushort )IndicClusterState.StartState; 
        private const ushort ToStartLinkedState = (ushort )IndicClusterState.StartLinkedState;
        private const ushort StartCluster  = ToStartState | StartOfCluster;

        private const ushort ToNBSPState  = (ushort )IndicClusterState.NBSPState; 
        private const ushort StartNBSPCluster  = ToNBSPState | StartOfCluster;
 
        private const ushort ToVowelState  = (ushort )IndicClusterState.VowelState; 
        private const ushort StartVowelCluster  = ToVowelState | StartOfCluster;
 
        private const ushort ToConsonantState  = (ushort )IndicClusterState.ConsonantState;
        private const ushort StartConsonantCluster  = ToConsonantState | StartOfCluster;
        private const ushort ToRaState  = (ushort )IndicClusterState.RaState;
        private const ushort StartRephConsonantCluster = ToRaState | StartOfCluster; 

        // These state machine entries are used to respond to ZWJ/ZWNJ characters in the text stream 
        private const ushort ToZWJState  = (ushort )IndicClusterState.ZWJState; 
        private const ushort ToVowelZWJState  = (ushort )IndicClusterState.VowelZWJState;
        private const ushort ToStandaloneState  = (ushort )IndicClusterState.StandaloneState; 
        private const ushort StartStandaloneCluster  = ToStandaloneState + StartOfCluster;

        private const ushort ToZWJHalantState  = (ushort )IndicClusterState.ZWJHalantState;
        private const ushort ToMatraState  = (ushort )IndicClusterState.MatraState; 
        private const ushort ToHalantState  = (ushort )IndicClusterState.HalantState;
        private const ushort ToRephState  = (ushort )IndicClusterState.RephState; 
 
        private const ushort ToVedicState  = (ushort )IndicClusterState.VedicState;
        private const ushort To2ndVedicState  = (ushort )IndicClusterState.SecondVedicState; 
        private const ushort ToVowelSignState  = (ushort )IndicClusterState.VowelSignState;

        private const ushort ToFinalState  = (ushort )IndicClusterState.FinalState;
        private const ushort EndOfCluster  = ToStartState; 
//        private const ushort StartNextCluster  = StartOfCluster;
 
        // The syllable state machine state table.  For each current state 
        // there are 16 entries - one for each char class (see IndicCharClass, 14 classes) and
        // 2 extra for padding - so that the first entry for any current state can 
        // be found at "current state << 4"
        private static readonly ushort[] IndicClusterStateTable  = //new IndicSyllableState[]
        {
            // State Table Entry        Input Char Class 
            StartCluster | InvalidBase,// Halant    (mark)
            StartCluster | InvalidBase,// Vedic     (mark) 
            StartCluster | InvalidBase,// VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding

            // StartLinkedState                  Input Char Class
            ToZWJHalantState,          // Halant    (mark) 
            ToStartLinkedState,        // Vedic     (mark)
            ToStartLinkedState,        // VowelSign (mark) 
            ToMatraState,              // Matra     (mark) 
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            ToStartLinkedState,        // Nukta     (mark) 
            StartVowelCluster,         // Vowel     (base)
            ToZWJState,                // ZWJ 
            StartCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 

            // NBSPState                         Input Char Class
            EndOfCluster,              // Halant    (mark)
            EndOfCluster,              // Vedic     (mark) 
            EndOfCluster,              // VowelSign (mark)
            ToMatraState,              // Matra     (mark) 
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            EndOfCluster,              // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            ToStandaloneState,         // ZWJ
            EndOfCluster,              // ZWNJ 
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
            // Standalone                  Input Char Class
            EndOfCluster,              // Halant    (mark)
            EndOfCluster,              // Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign  (mark) 
            StartCluster | InvalidBase,// Matra      (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            EndOfCluster,              // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding 

            // VowelState                 Input Char Class 
            ToHalantState,             // Halant    (mark)
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark) 
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            ToVowelState,              // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            ToVowelZWJState,           // ZWJ
            ToVowelZWJState,           // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 
 
            // VowelZWJState                 Input Char Class
            ToHalantState,             // Halant    (mark) 
            StartCluster | InvalidBase,// Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark)
            ToMatraState,              // Matra     (mark)
            ToVowelState,              // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            ToVowelState,              // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
            // ConsonantState           Input Char Class 
            ToHalantState,             // Halant    (mark)
            ToVedicState,              // Vedic     (mark) 
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark)
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta -- 
            ToConsonantState,          // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            ToZWJState,                // ZWJ
            ToConsonantState,          // ZWNJ
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 

            // RaState                 Input Char Class 
            ToRephState,               // Halant    (mark) 
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark) 
            ToMatraState,              // Matra     (mark)
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            ToRaState,                 // Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            ToZWJState,                // ZWJ 
            ToConsonantState | ZWControlChar, // ZWNJ
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding
 
            // HalantState              Input Char Class
            StartCluster | InvalidBase,// Halant    (mark) 
            ToVedicState,              // Vedic     (mark) 
            ToVowelSignState,          // VowelSign (mark)
            StartCluster | InvalidBase,// Matra     (mark) 
            ToConsonantState,          // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  --
            ToConsonantState,          // Consonant -- 
            ToConsonantState,          // Consonant with Nukta --
            ToHalantState,             // Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base) 
            ToZWJState,                // ZWJ
            EndOfCluster | ZWControlChar, // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding

            // RephState                Input Char Class 
            StartCluster | InvalidBase,// Halant    (mark)
            ToVedicState,              // Vedic     (mark) 
            ToVowelSignState,          // VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark)
            ToConsonantState,          // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  --
            ToConsonantState,          // Consonant --
            ToConsonantState,          // Consonant with Nukta -- 
            ToRephState,               // Nukta     (mark)
            ToVowelState,              // Vowel     (base) 
            ToZWJState,                // ZWJ 
            EndOfCluster | ZWControlChar, // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding

            // VowelSignState                Input Char Class
            StartCluster | InvalidBase,// Halant    (mark) 
            ToVedicState,              // Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark) 
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            EndOfCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 

            // VedicState                Input Char Class
            StartCluster | InvalidBase,// Halant    (mark)
            To2ndVedicState,           // Vedic     (mark) 
            StartCluster | InvalidBase,// VowelSign (mark)
            StartCluster | InvalidBase,// Matra     (mark) 
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            EndOfCluster,              // ZWNJ 
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
            // SecondVedicState                Input Char Class
            StartCluster | InvalidBase,  // Halant    (mark)
            EndOfCluster,              // Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            EndOfCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding 

            // ZWState                Input Char Class 
            ToZWJHalantState,          // Halant    (mark)
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark) 
            ToConsonantState,          // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  -- 
            ToConsonantState,          // Consonant --
            ToConsonantState,          // Consonant with Nukta -- 
            ToZWJState,                // Nukta     (mark)
            ToVowelState,              // Vowel     (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 
 

            // ZWHalant                Input Char Class 
            StartCluster | InvalidBase,// Halant    (mark)
            StartCluster | InvalidBase,// Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark)
            StartCluster | InvalidBase,// Matra     (mark) 
            ToConsonantState,          // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  -- 
            ToConsonantState,          // Consonant --
            ToConsonantState,          // Consonant with Nukta -- 
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 
 
            // MatraState                Input Char Class
            ToMatraState,              // Halant    (mark) 
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            ToMatraState,              // Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
 
            //FinalState                Input Char Class
            // This state is identical to StartState 
            StartCluster | InvalidBase,  // Halant    (mark)
            StartCluster | InvalidBase,  // Vedic    (mark)
            StartCluster | InvalidBase,  // VowelSign (mark)
            StartCluster | InvalidBase,  // Matra 
            StartRephConsonantCluster,  // Ra --
            StartCluster,                // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,            // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            StartCluster | InvalidBase,  // Nukta    (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,          // ZWNJ 
            StartCluster,                // Unknown class --
            NoChange,NoChange,         // padding 
 
        };
    } 

    internal struct IndicClusterCop
    {
        IndicScriptIndices _scriptIx; 
        ConsonantState    _consonantState;
 
        IndicCharClassifier _charConverter; 
        IndicFormFlags     _matraPositionFlags;
 
        Feature[]   _textFeatures;
        uint[]      _featureTags;
        IndicFontClient _fontClient;
 
        ushort     _firstCharIx;
 
        ushort     _lastHalantOffset; 
        ushort     _lastConsonantOffset;
        ushort     _lastNuktaOffset; 
        ushort     _preMainMatraOffset;
        ushort     _vowelOffset;
        ushort     _clusterSize;
        ushort     _unmappedGlyphsCount; 

        ushort      _movedToBeforeSubCount; 
        ushort      _movedToBeforePostCount; 
        ushort      _movedToAfterSubCount;
        ushort      _movedToAfterPostCount; 
        ushort      _movedToBeforeEndCount;

        ushort     _halfOffset;
        ushort     _mainOffset; 
        ushort     _subOffset;
        ushort     _postOffset; 
        ushort     _lastMainOffset; 
        ushort     _rephOffset;
        ushort     _postbaseRaOffset; 
        ushort     _ZWJOffset;

        bool       _useV2FontRules;
 
        bool       _isVowelPresent;
        bool       _isZWJPresent; 
        bool       _isMatraPresent; 
        bool       _isSplitMatraPresent;
 

        bool       _isNuktaPresent;
        bool       _isHalantActive;
        bool       _isMatraReorderPending; 
        bool       _isPostMatraHalantPresent;
        bool       _isPostMatraNuktaPresent; 
        bool       _isRephReorderPending; 
        bool       _isVattuPresent;
 
        bool       _isReorderingNecessary;
        bool       _gurmukhiRephGoesAfterPostMatra;

        IndicRepositioningClass _lastMatraRepositioningClass; 

        private const CharShapeInfo RepositioningClassMask = 
                            (CharShapeInfo)IndicRepositioningClass.RepositioningClassMask; 

        ///  
        /// Critical - calls critical code
        /// 
        [SecurityCritical]
        public IndicClusterCop( IndicFontClient fontClient ) 
        {
            _firstCharIx = 0; 
            _vowelOffset = _mainOffset = _lastMainOffset = 0; 
            _subOffset = _postOffset = 0;
            _rephOffset = _postbaseRaOffset = 0; 
            _lastConsonantOffset = _lastHalantOffset = _lastNuktaOffset = 0;
             _preMainMatraOffset = 0;
            _ZWJOffset = 0;
            _halfOffset = 0xffff; 

            _movedToBeforeSubCount  = 0; 
            _movedToBeforePostCount = 0; 
            _movedToAfterSubCount  = 0;
            _movedToAfterPostCount = 0; 
            _movedToBeforeEndCount = 0;
            _isReorderingNecessary = false;

            _consonantState = ConsonantState.Start; 
            _isVowelPresent = false;
            _isZWJPresent = false; 
            _isNuktaPresent = false; 
            _isHalantActive = false;
            _isRephReorderPending = false; 
            _isVattuPresent = false;
            _gurmukhiRephGoesAfterPostMatra = false;

            _matraPositionFlags = 0; 
            _isMatraPresent = _isSplitMatraPresent = false;
            _isMatraReorderPending = false; 
            _isPostMatraNuktaPresent = _isPostMatraHalantPresent = false; 

            _lastMatraRepositioningClass = IndicRepositioningClass.AtStart; 

            _clusterSize = 0;
            _unmappedGlyphsCount = 0;
            _textFeatures = new Feature[1]; 
            _textFeatures[0] = new Feature(0,0,0,0);
 
            _fontClient = fontClient; 
            _charConverter = fontClient.IndicCharConverter;
            _featureTags = fontClient.ScriptGsubFeaturesList; 

            // new style fonts expect post-main consonant features
            // to be applied starting at the preceding halant, whereas
            // the old style fonts expect the post-main consonants to 
            // be reversed from h+C to C+h before applying the features
            _useV2FontRules = fontClient.IsIndicV2Font; 
            _scriptIx = _charConverter.ScriptIx; 

 
        }

        public void ResetClusterInfo( ushort firstCharInCluster )
        { 
            if (_consonantState != ConsonantState.Start)
            { 
                _consonantState = ConsonantState.Start; 

                _isVowelPresent = false; 
                _isZWJPresent = false;
                _isNuktaPresent = false;
                _isHalantActive = false;
                _isVattuPresent = false; 
                _gurmukhiRephGoesAfterPostMatra = false;
 
                _mainOffset = _lastMainOffset = 0; 
                _subOffset = _postOffset = 0;
                _rephOffset = 0; 
                _lastConsonantOffset = 0;
                _preMainMatraOffset = 0;

                _halfOffset = 0xffff; 
            }
 
            _firstCharIx = firstCharInCluster; 
            _matraPositionFlags = 0;
            _lastHalantOffset = _lastNuktaOffset = 0; 

            _isMatraPresent = _isSplitMatraPresent = _isPostMatraNuktaPresent = _isPostMatraHalantPresent = false;
            _clusterSize = 0;
            _unmappedGlyphsCount = 0; 
        }
 
        ///  
        ///   IndicClusterCop.ApplyClusterFeatures - finalize the preceding cluster.  This means do
        ///         any reordering and apply features as appropriate 
        /// 
        /// 
        ///   This routine reorders matras, applies all the features that need to be applied,
        ///   and does any final reordering 
        /// 
        /// state flags for current character appropriate to new state 
        ///  
        /// Critical - calls critical code, uses unsafe accessors
        ///  
        [SecurityCritical]
        internal void ApplyClusterFeatures(ref ShapingWorkspace currentRun )
        {
            if (_clusterSize > 1) 
            {
                FinalizeOffsets(  ref currentRun, _clusterSize, true ); // make sure cluster offsets are right 
 
                bool isInitFormPending = false;
                ushort preBaseRephGlyphIx = 0; 
                ushort preBaseRephGlyph = 0;

                // apply features
                for (int i = 0; i < _featureTags.Length; ++i) 
                {
                    _textFeatures[0].Tag = _featureTags[i]; 
                    _textFeatures[0].Parameter = 1; 
                    _textFeatures[0].StartIndex = _firstCharIx;
                    _textFeatures[0].Length = _clusterSize; 

                    switch ( _featureTags[i] )
                    {
                        case (uint)FeatureTags.InitialForms: 
                            // this better be the last Bengali feature
                            Debug.Assert( i + 1 == _featureTags.Length, "init isn't last feature!") ; 
                            _textFeatures[0].Length = 1; 
                            isInitFormPending = true;
                            continue;        // wait till final reordering is done 

                        case (uint)FeatureTags.NuktaForms:
                            if (!_isNuktaPresent)
                            { 
                                continue;   // don't do this one
                            } 
                            break; 

                        case (uint)FeatureTags.Akhands: 
                            // Since I've changed the order of applying reph and akhand features
                            // I don't think I need to worry about applying this feature to the
                            // reph because the reph has already formed (so shouldn't participate
                            // in any lookups for this feature) 
                            if (/*_rephOffset > 0 || */_postbaseRaOffset > 0)
                            { 
                                /*ushort startOffset = 0;     // 
                                if (_rephOffset > 0 &&
                                    (_postbaseRaOffset == 0 || _postbaseRaOffset > _rephOffset)) 
                                {
                                    // don't apply this feature to reph (or the prebase ra, whichever's
                                    // first) Since there may be characters after the reph, apply to the
                                    // reph, then apply to everything after the reph... 
                                    _textFeatures[0].Length = _rephOffset;
                                    _fontClient.SubstituteGlyphs( ref currentRun, 
                                                                 _textFeatures, 
                                                                 1 );
 
                                    // start again past the reph.  If necessary, use LigatureCounts to take
                                    // account of the possibility that we've said the reph is a single
                                    // character due to split matra adjustments
                                    startOffset = 
                                        (ushort)(_rephOffset + (!_isSplitMatraPresent ? (ushort)1 :
                                        currentRun.GlyphInfoList.LigatureCounts[firstGlyphIx + _rephOffset])); 
 
                                    _textFeatures[0].Length = (ushort)(_clusterSize - startOffset);
 
                                }
                                if (_postbaseRaOffset > 0)
                                {
                                    if (_postbaseRaOffset <= startOffset) 
                                    {
                                        continue;   // must be right after reph.  We're done with akhands. 
                                    } 
                                */
                                    // don't apply this feature to Malayalam prebase ra 
                                    _textFeatures[0].Length = (ushort)(_postbaseRaOffset );
                                /*}

                                if (startOffset >= _clusterSize) 
                                {
                                    continue; 
                                } 
                                _textFeatures[0].StartIndex = (ushort)(_firstCharIx + startOffset);
                                 */ 

                            }
                            break;
 
                        case (uint)FeatureTags.RephForm:
                            if (_rephOffset == 0) 
                            { 
                                continue;   // don't do this one
                            } 
                            _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _rephOffset); // only apply to reph
                            if (_isSplitMatraPresent)
                            {
                                ushort rephGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _rephOffset)); 
                                _textFeatures[0].Length =
                                        currentRun.GlyphInfoList.LigatureCounts[rephGlyphIx]; 
 
                            }
                            else 
                            {
                                _textFeatures[0].Length = 2;
                            }
                            break; 

                        case (uint)FeatureTags.PrebaseForms: 
                            if ( _postbaseRaOffset == 0 || !_useV2FontRules ) 
                            {
                                continue;   // don't do this one 
                            }
                            else
                            {
                                ushort prebaseRephIx = (ushort)(_firstCharIx + _postbaseRaOffset); 
                                preBaseRephGlyphIx = currentRun.GetGlyphIx(prebaseRephIx);
                                preBaseRephGlyph = currentRun.GetGlyph(preBaseRephGlyphIx); 
 
                                _textFeatures[0].StartIndex = prebaseRephIx;
                                _textFeatures[0].Length = _isSplitMatraPresent ? 
                                    currentRun.GlyphInfoList.LigatureCounts[preBaseRephGlyphIx] :
                                    (ushort) 2;
                            }
                            break; 

                        case (uint)FeatureTags.BelowBaseForms: 
                            if (_subOffset > 0 || _lastMainOffset > _mainOffset) 
                            {
                              // apply to consonants pre main to last sub 
                              _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _mainOffset + 1);
                              // apply through sub consonants
                            }
                            else if (!_isVattuPresent) 
                            {
                                continue;       // don't apply this one 
                            } 

                            _textFeatures[0].Length = 
                                (ushort)((_postOffset > 0 ? _postOffset : _clusterSize)
                                            - ( _mainOffset + 1));
                            break;
 
                        case (uint)FeatureTags.HalfForms:
                            if (_lastMainOffset <= _halfOffset) 
                            { 
                                continue;       // don't apply this one
                            } 

                             // apply to consonants from first half to last main
                            _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _halfOffset);
                             _textFeatures[0].Length = (ushort)(_lastMainOffset - _halfOffset); 
                            break;
 
                        case (uint)FeatureTags.PostbaseForms: 
                            // pre-Vista kartika font uses PSTF feature to form prebase rephs
                            if ( _postbaseRaOffset != 0 && !_useV2FontRules) 
                            {
                                ushort prebaseRephIx = (ushort)(_firstCharIx + _postbaseRaOffset);
                                preBaseRephGlyphIx = currentRun.GetGlyphIx(prebaseRephIx);
                                preBaseRephGlyph = currentRun.GetGlyph(preBaseRephGlyphIx); 
                                if (_postbaseRaOffset < _postOffset)
                                { 
                                    _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _postbaseRaOffset); 
                                    _textFeatures[0].Length = (ushort)(_clusterSize - _postbaseRaOffset);
                                    break; 
                                }
                            }
                            else if (_postOffset == 0)
                            { 
                                continue;       // don't apply this one
                            } 
 
                             // apply to post consonants
                            _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _postOffset); 
                            _textFeatures[0].Length = (ushort)(_clusterSize - _postOffset);


                            break; 

                        case (uint)FeatureTags.VattuVariants: 
                            if (!(_isVattuPresent || _subOffset > 0)) 
                            {
                                continue;       // don't apply this one 
                            }
                            break;

                         default: 
                            break;
                        } 
 
                        //  Apply the Indic Text Features from currentRun
                        _fontClient.SubstituteGlyphs( ref currentRun, 
                                                     _textFeatures,
                                                     1 );
                        if (preBaseRephGlyphIx != 0)
                        { 
                            // check if the first prebase ra glyph is changed.  If not, prebase ra
                            // didn't form 
                            if (currentRun.GetGlyph(preBaseRephGlyphIx) == preBaseRephGlyph) 
                            {
                                _postbaseRaOffset = 0; 
                            }
                            preBaseRephGlyphIx = 0;
                        }
                } 

                // do post-feature reordering 
                ReorderClusterFinal( ref currentRun ); 

                if (isInitFormPending) 
                {
                    //  Apply the Bengali init feature to the first character
                    _fontClient.SubstituteGlyphs( ref currentRun, _textFeatures, 1 );
                } 
            }
            else 
            { 
                // the previous cluster is a single character cluster.  The only thing we need
                // to do is check if its a dotted circle with a prebase matra 
                ushort previousCharIx =
                    currentRun.IsFinished ? currentRun.CurrentCharIx : currentRun.PreviousCharIx;
                CharShapeInfo previousShape = currentRun.GetShapeInfo(previousCharIx);
                if ((previousShape & CharShapeInfo.RequiresInsertedBase) == 
                                                    CharShapeInfo.RequiresInsertedBase)
                { 
                    // the cluster is a "dotted circle" cluster.  So, check for prebase 
                    // matra.  First test is for a split matra.  If its a split matra
                    // use the repositioning info for the first component matra 
                    if ((IndicCharClass)(previousShape & IndicShape.IndicCharClassMask)
                                                                        == IndicCharClass.Matra)
                    {
                        char previousChar = currentRun.GetChar(previousCharIx); 
                        CharShapeInfo matraShapeInfo = _charConverter.ToShapeInfo(previousChar);
                        bool isSplitMatra = 
                            ((matraShapeInfo & IndicShape.ShapeIncludesPositioningInfoFlag) == 0); 
                        if (isSplitMatra)
                        { 
                            char [] matraComponents =
                                _charConverter.ToDecompositionList(previousChar);
                            if (matraComponents != null)
                            { 
                               matraShapeInfo = (CharShapeInfo)_charConverter.ToRepositioningClass(matraComponents[0]);
                            } 
                        } 
                        else
                        { 
                               matraShapeInfo = (CharShapeInfo)GetRepositioningClass(matraShapeInfo);
                        }

                        // Now check if matra's a before main matra... 
                        if ((ushort)matraShapeInfo <=
                             (ushort)IndicRepositioningClass.BeforeMain) 
                        { 
                            // the matra is "before main" so
                            // swap the dotted circle glyph and its first matra 
                            ushort dottedCircleGlyphIx = currentRun.GetGlyphIx(previousCharIx);
                            currentRun.SetGlyph(dottedCircleGlyphIx,
                                currentRun.GetGlyph((ushort)(dottedCircleGlyphIx + 1)));
                            currentRun.SetGlyph((ushort)(dottedCircleGlyphIx + 1), 
                                                        _fontClient.DottedCircleGlyph);
                        } 
                    } 

                } 
            }

        }
 
        /// 
        /// Critical - calls critical code 
        ///  
        [SecurityCritical]
        private bool ReorderClusterFinal(ref ShapingWorkspace currentRun) 
        {
                // now that initial shaping has been done, make adjustments if
                // necessary to properly reposition matras/reph when we have
                // a non-conjunct forming sequence of pre-sub consonants 

            if (_isReorderingNecessary) 
            { 
                ushort toPosition = 0;
                bool foundReorderingAnchor = FindLateRepositionAnchor(ref currentRun); 

                // do the reordering necessary...
                // (first do matras, vowel signs, then reph)
                if (_isMatraReorderPending) 
                {
                   if (foundReorderingAnchor) 
                   { 
                        toPosition = _lastMainOffset;
                        _mainOffset -= 1; 

                       ushort glyphsCount = 1;
                       ushort matraGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _preMainMatraOffset));
                       while (matraGlyphIx + glyphsCount < currentRun.GlyphsCount && 
                              currentRun.GlyphInfoList.FirstChars[matraGlyphIx + glyphsCount] ==
                              currentRun.GlyphInfoList.FirstChars[matraGlyphIx]) 
                       { 
                            glyphsCount += 1;
                       } 
                       RepositionGlyphs( ref currentRun, _preMainMatraOffset, toPosition,
                                          1, glyphsCount);
                   }
 
                   if (_isRephReorderPending && _postbaseRaOffset == 0)
                   { 
                        foundReorderingAnchor = FindLateRepositionAnchor(ref currentRun); 
                   }
 
                    _preMainMatraOffset = 0;
                    _isMatraReorderPending = false;
                }
 

                // if there's a pre-base reph to move, nows the time... 
                if (_postbaseRaOffset > 0) 
                {
                    // move the post base ra to the appropriate main consonant 

                    toPosition = _mainOffset;
                    if (toPosition < _postbaseRaOffset)
                    { 
                        ushort raGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _postbaseRaOffset));
                        ushort charCount = currentRun.GlyphInfoList.LigatureCounts[raGlyphIx]; 
 
                        if (_postbaseRaOffset > _lastMainOffset)
                        { 
                            toPosition = _lastMainOffset;
                            _lastMainOffset += charCount;
                        }
                        if (_postbaseRaOffset > _rephOffset && toPosition <= _rephOffset) 
                        {
                            _rephOffset += charCount; 
                        } 

                        RepositionGlyphs( ref currentRun, _postbaseRaOffset, toPosition, charCount, 1 ); 
                    }

                   if (_isRephReorderPending)
                   { 
                        foundReorderingAnchor = FindLateRepositionAnchor(ref currentRun);
                   } 
                   _postbaseRaOffset = 0; 
                }
 
                // And, finally, if there's a reph to revisit do it now...
                if (_isRephReorderPending)
                {
                   _isRephReorderPending = false; 

                   if ( foundReorderingAnchor && _rephOffset > _lastMainOffset ) 
                   { 
                        ushort rephGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _rephOffset));
                        ushort charCount = currentRun.GlyphInfoList.LigatureCounts[rephGlyphIx]; 
                        RepositionGlyphs( ref currentRun, _rephOffset, _lastMainOffset, charCount, 1 );
                   }
                }
 

            } 
 

            // done reordering, reset all these counters... 
            _movedToBeforeSubCount  = 0;
            _movedToBeforePostCount = 0;
            _movedToAfterSubCount  = 0;
            _movedToAfterPostCount = 0; 
            _movedToBeforeEndCount = 0;
 
            _isMatraPresent = _isPostMatraNuktaPresent = _isPostMatraHalantPresent = false; 

            _isReorderingNecessary = false; 

            return true;
        }
 
        private ushort GetRepositionOffsetAndUpdateCluster (IndicRepositioningClass repositionClass,
                                                                        bool isReph, 
                                                                        ushort charsMovingCount) 
        {
            ushort offsetFromEnd = 0; 
            ushort toPosition = 0;
            bool   useOffsetFromEnd = false;

            switch (repositionClass) 
            {
                case IndicRepositioningClass.AtStart: 
                    toPosition = _halfOffset <= _mainOffset ? _halfOffset : _mainOffset; 
                    goto MovingToBeforeMain;
 
                case IndicRepositioningClass.BeforeMain:
                    toPosition = _mainOffset;

MovingToBeforeMain: 
                    if (_lastMainOffset > toPosition)
                    { 
                        // note this position (we're still not sure if the main 
                        // consonants will be forming a conjunct form).  If
                        // last main > main consonant ix, then we'll need to 
                        // check if matra needs reordering after features are
                        // applied.  (if Malayam, check for reordering if
                        // last main > half offset)
                        if (toPosition == _mainOffset || _scriptIx == IndicScriptIndices.Malayalam) 
                        {
                            _preMainMatraOffset = toPosition; 
                            _isReorderingNecessary = true; 
                            _isMatraReorderPending = true;
                        } 
                    }

                    // adjust the premain and "main" consonants' offsets
                    // so that they are now after this newly inserted matra/sign/prebase ra 
                    if (_halfOffset == toPosition)
                    { 
                        _halfOffset += charsMovingCount; 
                    }
 
                    _mainOffset += charsMovingCount;
                    _lastMainOffset += charsMovingCount;

                    break; 

                case IndicRepositioningClass.AfterMain: 
                    if (_subOffset > 0) 
                    {
                        toPosition = (ushort)(_subOffset - _movedToBeforeSubCount); 
                    }
                    else if (_postOffset > 0)
                    {
                        toPosition = (ushort)(_postOffset - _movedToBeforePostCount); 
                    }
                    else 
                    { 
                        useOffsetFromEnd = true;
                        offsetFromEnd = (ushort) 
                            (   _movedToBeforeSubCount +
                                _movedToAfterSubCount  +
                                _movedToBeforePostCount +
                                _movedToAfterPostCount + 
                                _movedToBeforeEndCount );
                    } 
                    break; 

                case IndicRepositioningClass.BeforeSub: 

                    if (_subOffset > 0)
                    {
                        toPosition = _subOffset; 
                    }
                    else if (_postOffset > 0) 
                    { 
                        toPosition = (ushort)(_postOffset - _movedToBeforePostCount);
                    } 
                    else
                    {
                        useOffsetFromEnd = true;
                        offsetFromEnd = (ushort) 
                            (   _movedToAfterSubCount  +
                                _movedToBeforePostCount + 
                                _movedToAfterPostCount + 
                                _movedToBeforeEndCount );
 
                    }

                    _movedToBeforeSubCount += charsMovingCount;
                    break; 

 
                case IndicRepositioningClass.AfterSub: 
                    if ( _postOffset > 0)
                    { 
                         toPosition = (ushort)(_postOffset - _movedToBeforePostCount);  // put before post offset
                    }
                    else
                    { 
                        useOffsetFromEnd = true;
                        offsetFromEnd = (ushort) 
                                        ( _movedToBeforePostCount + 
                                          _movedToAfterPostCount +
                                          _movedToBeforeEndCount ); 
                    }

                    _movedToAfterSubCount += charsMovingCount;
                    break; 

                case IndicRepositioningClass.BeforePost: 
                    if ( _postOffset > 0) 
                    {
                         toPosition = _postOffset;  // put before post offset 
                    }
                    else
                    {
                        useOffsetFromEnd = true; 

                        if (isReph) 
                        { 
                            // special case for Gurmukhi - when there's no post consonant but
                            // there is an after-post matra, then move the reph after the 
                            // matra (unless its one of the two matras,  0xa3e or 0xa40)
                            if (_gurmukhiRephGoesAfterPostMatra)
                            {
                                _gurmukhiRephGoesAfterPostMatra = false; 

                                offsetFromEnd = (ushort)( _movedToBeforeEndCount ); 
                                _movedToAfterPostCount += charsMovingCount; 
                                break;
                            } 
                        }

                        offsetFromEnd = (ushort)
                                        ( _movedToAfterPostCount + 
                                          _movedToBeforeEndCount );
                   } 
 

                    _movedToBeforePostCount += charsMovingCount; 
                    break;

                case IndicRepositioningClass.AfterPost:
                    useOffsetFromEnd = true; 
                    offsetFromEnd = (ushort)( _movedToBeforeEndCount );
 
                    _movedToAfterPostCount += charsMovingCount; 
                    break;
 
                case IndicRepositioningClass.BeforeEnd:
                    useOffsetFromEnd = true;
                    offsetFromEnd = 0;
 
                    _movedToBeforeEndCount += charsMovingCount;
                    break; 
 
                default:
                    throw new NotSupportedException(); 

            }

            // default return 
            if (useOffsetFromEnd)
            { 
                toPosition = (ushort)( _clusterSize + _unmappedGlyphsCount - offsetFromEnd ); 
            }
 
            if (charsMovingCount > 0)
            {
                if (isReph)
                { 
                    // adjust all the cluster offsets for a reph being positioned before feature
                    // application 
                    _halfOffset -= charsMovingCount; 
                    _mainOffset -= charsMovingCount;
                    _lastMainOffset -= charsMovingCount; 
                    if (_postbaseRaOffset > 0 && _postbaseRaOffset <= toPosition)
                    {
                        if (_postbaseRaOffset == toPosition)
                        { 
                            toPosition += charsMovingCount;    // if positioning reph in front of prebase ra, move past it
                        } 
                        else if ((ushort)(_postbaseRaOffset + 1) == toPosition) 
                        {
                            toPosition += 1;    // if positioning reph in front of prebase ra, move past it 
                        }
                        _postbaseRaOffset -= charsMovingCount;
                    }
 
                    if (_subOffset > 0 && _subOffset < toPosition) _subOffset -= charsMovingCount;
                    if (_postOffset > 0 && _postOffset < toPosition) _postOffset -= charsMovingCount; 
                    if (_preMainMatraOffset > 0 && _preMainMatraOffset < toPosition) _preMainMatraOffset -= charsMovingCount; 

                    if (_isPostMatraNuktaPresent && _lastNuktaOffset < toPosition) _lastNuktaOffset -= charsMovingCount; 
                    _rephOffset = (ushort)(toPosition - charsMovingCount);
                }
                else
                { 
                    // make adjustments for any matra, sign
                    if (_subOffset > 0 && _subOffset >= toPosition) _subOffset += charsMovingCount; 
                    if (_postOffset > 0 && _postOffset >= toPosition) _postOffset = (ushort)((int)_postOffset + charsMovingCount); 
                    if (_postbaseRaOffset > 0 && _postbaseRaOffset >= toPosition) _postbaseRaOffset = (ushort)((int)_postbaseRaOffset + charsMovingCount);
                    if (_rephOffset > 0 && _rephOffset >= toPosition) _rephOffset = (ushort)((int)_rephOffset + charsMovingCount); 
                    if (_isPostMatraNuktaPresent && _lastNuktaOffset >= toPosition) _rephOffset = (ushort)((int)_rephOffset + charsMovingCount);
                }
            }
 
            return toPosition;
        } 
 

        ///  
        /// Critical - calls critical code
        /// 
        [SecurityCritical]
        private void RepositionGlyphs( ref ShapingWorkspace currentRun, 
                                      ushort fromPosition, ushort toPosition,
                                      ushort charsToMove, ushort glyphsToMove) 
        { 
            // do the glyph move
            GlyphInfoList glyphs = currentRun.GlyphInfoList; 
            UshortList charMap = currentRun.CharMap;
            ushort nextGlyphIx, previousGlyphIx;
            ushort nextCharIx, previousCharIx;
 
            // set up the "glyphs to move" variables...
            fromPosition += _firstCharIx; 
            toPosition += _firstCharIx; 
            ushort firstGlyphToMove = currentRun.GetGlyphIx(fromPosition);
 
            if (toPosition > fromPosition)
            {
                   // moving glyphs backwards (toward the end of the cluster)
                   // This must be a before main matra that needs to be moved 
                   // back onto the last component in a non-conjunct forming
                   // multi-consonant sequence. 
                ushort charMoveDistance = (ushort)(toPosition - fromPosition); 
                ushort glyphMoveToPosition = (ushort)
                   (( toPosition == currentRun.CharsCount ? 
                                    currentRun.GlyphsCount :
                                    currentRun.GetGlyphIx( toPosition ) ) - 1 );
                ushort glyphMoveDistance = (ushort)(glyphMoveToPosition - firstGlyphToMove);
 
                if (glyphMoveDistance == 0)
                { 
//                    Debug.Assert (glyphMoveDistance > 0,"glyph move distance is zero!"); 
                    return;
                } 

                   // go through all the glyphs, removing one at a time from the front
                   // of the block and reinserting it at the back of the block
                 for (int i = 0; i < glyphsToMove; ++i) 
                 {
                   ushort movingGlyph      = glyphs.Glyphs[firstGlyphToMove]; 
                   ushort movingLigCount   = glyphs.LigatureCounts[firstGlyphToMove]; 
                   ushort movingFlags      = glyphs.GlyphFlags[firstGlyphToMove];
 
                   for ( nextGlyphIx = firstGlyphToMove;
                         nextGlyphIx < glyphMoveToPosition;
                         ++nextGlyphIx)
                   { 
                       previousGlyphIx = (ushort)(nextGlyphIx + 1);
 
                       glyphs.Glyphs[nextGlyphIx] = glyphs.Glyphs[previousGlyphIx]; 
                       glyphs.LigatureCounts[nextGlyphIx] = glyphs.LigatureCounts[previousGlyphIx];
                       glyphs.GlyphFlags[nextGlyphIx] = glyphs.GlyphFlags[previousGlyphIx]; 
                   }

                   glyphs.Glyphs[glyphMoveToPosition] = movingGlyph;
                   glyphs.LigatureCounts[glyphMoveToPosition] =  movingLigCount; 
                   glyphs.GlyphFlags[glyphMoveToPosition] = movingFlags;
                 } 
 
                // go through the first chars now and fix 'em up.  The intent is to
                // have the effect of removing "charsToMove" characters from the front 
                // of the block of characters and add them to the back of the character
                // block.  Of course, we aren't really moving the characters, but just
                // setting the glyphs' "first char" pointer and the charmap values as
                // if we had moved the characters.  Similarly, we have removed "glyphsToMove" 
                // glyphs from the front of the glyphs and added it to the back.
                // We didn't do this in the loop above because we might do the loop several 
                // times (if glyphsToMove is greater than one). 
                nextCharIx = (ushort)(toPosition - charsToMove);
                nextGlyphIx = firstGlyphToMove; 
                for (int i = 0; i < glyphMoveDistance; ++i, ++nextGlyphIx)
                {
                    previousCharIx = glyphs.FirstChars[ nextGlyphIx + glyphsToMove ];
                    if (previousCharIx >= fromPosition) 
                    {
                        glyphs.FirstChars[ nextGlyphIx ] = 
                                   (ushort)(previousCharIx - charsToMove); 
                    }
                    else 
                    {
                         glyphs.FirstChars[ nextGlyphIx ] = previousCharIx;
                    }
                } 
                // finish up the inserted (moved) glyphs. There's always one character
                // per glyph 
                while (nextGlyphIx <= glyphMoveToPosition) 
                {
                    glyphs.FirstChars[nextGlyphIx++] = nextCharIx++; 
                }

                // do the same thing for the charmap
                nextCharIx = fromPosition; 
                nextGlyphIx = (ushort)(glyphMoveToPosition - glyphsToMove);
                for (int i = 0; i < charMoveDistance; ++i, ++nextCharIx) 
                { 
                    previousGlyphIx = charMap[nextCharIx + charsToMove];
                    if (previousGlyphIx >= firstGlyphToMove) 
                    {
                        charMap[nextCharIx] = (ushort)(previousGlyphIx - glyphsToMove);
                    }
                    else 
                    {
                        charMap[nextCharIx] = previousGlyphIx; 
                    } 
                }
                while (nextCharIx < toPosition) 
                {
                   charMap [ nextCharIx++ ] = ++nextGlyphIx;
                }
            } 
            else if (toPosition < fromPosition)
            { 
                 // moving glyphs forward (toward the beginning of the cluster) 
                 // These are ra's and prebase ra's...
                 ushort glyphMoveToPosition = currentRun.GetGlyphIx( toPosition ); 
                 ushort charMoveDistance = (ushort)(fromPosition - toPosition);
                 ushort glyphMoveDistance = (ushort)(firstGlyphToMove - glyphMoveToPosition);
                 if (glyphMoveDistance == 0)
                 { 
//                    Debug.Assert (glyphMoveDistance > 0,"glyph move distance is zero!");
                    return; 
                 } 

 
                 for (int i = 0; i < glyphsToMove; ++i)
                 {
                   nextGlyphIx = (ushort)(firstGlyphToMove + i);
                   previousGlyphIx = (ushort)(nextGlyphIx - 1); 
                   ushort insertedGlyphPosition = (ushort)(glyphMoveToPosition + i);
 
                   ushort movingGlyph      = glyphs.Glyphs[nextGlyphIx]; 
                   ushort movingLigCount   = glyphs.LigatureCounts[nextGlyphIx];
                   ushort movingFlags      = glyphs.GlyphFlags[nextGlyphIx]; 

                   while (nextGlyphIx > insertedGlyphPosition)
                   {
                       glyphs.Glyphs[nextGlyphIx] = glyphs.Glyphs[previousGlyphIx]; 
                       glyphs.LigatureCounts[nextGlyphIx] =  glyphs.LigatureCounts[previousGlyphIx];
                       glyphs.GlyphFlags[nextGlyphIx--] = glyphs.GlyphFlags[previousGlyphIx--]; 
                   } 

                   glyphs.Glyphs[nextGlyphIx] = movingGlyph; 
                   glyphs.LigatureCounts[nextGlyphIx] =  movingLigCount;
                   glyphs.GlyphFlags[nextGlyphIx] = movingFlags;

                 } 

                // go through the first chars now and fix 'em up.  The intent is to 
                // have the effect of adding "charsToMove" characters to the front 
                // of the block of characters and removing them from the back of the character
                // block.  Of course, we aren't really moving the characters, but just 
                // setting the glyphs' "first char" pointer and the charmap values as
                // if we had moved the characters.  Similarly, we have removed "glyphsToMove"
                // glyphs from the back of the glyphs and added it to the front.
                // We didn't do this in the loop above because we might do the loop several 
                // times (if glyphsToMove is greater than one).
                nextGlyphIx = (ushort)(firstGlyphToMove + glyphsToMove - 1); 
                for (int i = 0; i < glyphMoveDistance; ++i, --nextGlyphIx) 
                {
                    previousCharIx = glyphs.FirstChars[ nextGlyphIx - glyphsToMove ]; 
                    if (previousCharIx >= toPosition )
                    {
                        glyphs.FirstChars[ nextGlyphIx ] =
                                (ushort)(previousCharIx + charsToMove); 
                    }
                    else 
                    { 
                        glyphs.FirstChars[ nextGlyphIx ] = previousCharIx;
                    } 
                }

                // finish up the inserted (moved) glyphs.  We either have one glyph for
                // multiple characters (like for reph's) or one glyph per character (as 
                // for normal matras), or zero characters for one glyph (for split matra
                // components) 
                nextCharIx = (ushort)(toPosition + charsToMove - 1); 
                while (nextGlyphIx > glyphMoveToPosition)
                { 
                    glyphs.FirstChars[nextGlyphIx--] = nextCharIx--;
                }
                glyphs.FirstChars[glyphMoveToPosition] = nextCharIx;
 
                // do the same thing for the charmap
                nextCharIx = (ushort)(fromPosition + charsToMove - 1); 
                nextGlyphIx = (ushort)(glyphMoveToPosition + glyphsToMove - 1); 
                for (int i = 0; i < charMoveDistance; ++i, --nextCharIx)
                { 
                    previousGlyphIx = charMap[nextCharIx - charsToMove];
                    if (previousGlyphIx >= glyphMoveToPosition)
                    {
                        charMap[nextCharIx] = (ushort)(previousGlyphIx + glyphsToMove); 
                    }
                    else 
                    { 
                        charMap[nextCharIx] = previousGlyphIx;
                    } 
                }
                while (nextCharIx > toPosition)
                {
                  charMap [ nextCharIx-- ] = nextGlyphIx; 
                  if (glyphsToMove > 1)  --nextGlyphIx;
                } 
                charMap [ toPosition ] = glyphMoveToPosition; 

            } 

        }

 
        /// 
        ///     IndicClusterCop.RepositionCharacters - 
        ///          . 
        /// 
        /// shaping currentRun 
        /// actual character ix (relative to start of cluster) to be moved
        /// "virtual" character ix for glyph to move to
        /// number of glyphs that are to be moved
        /// If there're no split matra components in the cluster then the 
        ///          "virtual" character ix corresponds exactly to the the
        ///          real character ix in the charmap.  If there are split matra 
        ///          components the "virtual" ix is the actual glyph ix (relative to 
        ///          first glyph in the cluster), but not correspond to the correct
        ///          character ix. 
        /// 
        /// Critical - calls critical code
        /// 
        [SecurityCritical] 
        private void RepositionCharacters (ref ShapingWorkspace currentRun,
                                      ushort fromPosition, 
                                      ushort toPosition, 
                                      ushort moveCount )
        { 
            if (fromPosition != toPosition)
            {
                ushort firstGlyphIx = currentRun.GetGlyphIx(_firstCharIx);
                ushort glyphsInCluster = (ushort)(_clusterSize + _unmappedGlyphsCount); 
                fromPosition = (ushort)(firstGlyphIx + fromPosition);
                toPosition = (ushort)(firstGlyphIx + toPosition); 
 
                if ( toPosition > fromPosition )    // if moving the reph (or halant for old style fonts)
                { 
                    --toPosition;                   // move to in front of to position
                    Debug.Assert(toPosition <
                                (ushort)(firstGlyphIx + _clusterSize + _unmappedGlyphsCount),
                                "Invalid reph/halant reposition"); 

                    ushort moveDistance = (ushort)( toPosition - fromPosition ); 
                    if (moveDistance > 0) 
                    {
                        if (_isZWJPresent) 
                        {
                            ushort zwjGlyphIx = (ushort)(firstGlyphIx + _ZWJOffset);
                            if(zwjGlyphIx > fromPosition && zwjGlyphIx <= toPosition)
                            { 
                               currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx] = ShapingWorkspace.GlyphFlagsNone;
                               if (_ZWJOffset >= moveCount) 
                               { 
                                   currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx - moveCount] =
                                                                ShapingWorkspace.GlyphFlagsZeroWidth; 
                                   _ZWJOffset -= moveCount;
                               }
                            }
                        } 

                        for (ushort i = 0; i < moveCount; ++i) 
                        { 
                            ushort movingGlyph = currentRun.GetGlyph(fromPosition);
 
                            currentRun.MoveGlyphs( fromPosition,
                                                   (ushort)(fromPosition + 1),
                                                   moveDistance );
 
                            // now update the repositioned glyph
                            currentRun.SetGlyph(toPosition, movingGlyph); 
                        } 
                    }
                } 
                else if (fromPosition > toPosition) // else if moving matra/sign
                {
                    Debug.Assert((ushort)(fromPosition + moveCount) <= currentRun.GlyphsCount &&
                                 (ushort)(fromPosition + moveCount) <= 
                                 (ushort)(firstGlyphIx + _clusterSize + _unmappedGlyphsCount + 1),
                                 "Invalid matra reposition"); 
                    ushort moveDistance = (ushort)( fromPosition - toPosition ); 

                    if (moveDistance > 0) 
                    {
                        if (_isZWJPresent)
                        {
                            ushort zwjGlyphIx = (ushort)(firstGlyphIx + _ZWJOffset); 
                            if(zwjGlyphIx >= toPosition && zwjGlyphIx < fromPosition)
                            { 
                               currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx] = ShapingWorkspace.GlyphFlagsNone; 
                               if (_ZWJOffset + moveCount < currentRun.GlyphsCount)
                               { 
                                   currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx + moveCount] =
                                                                ShapingWorkspace.GlyphFlagsZeroWidth;
                                   _ZWJOffset += moveCount;
                               } 
                            }
                        } 
 
                        for (ushort i = 0; i < moveCount; ++i)
                        { 

                            ushort matraGlyph = currentRun.GetGlyph(fromPosition);
                            currentRun.MoveGlyphs( (ushort)(toPosition + 1),
                                                   toPosition, 
                                                   moveDistance );
 
                            // now update the repositioned glyph 
                            currentRun.SetGlyph( toPosition, matraGlyph );
 
                            ++fromPosition;
                            ++toPosition;
                        }
                    } 

                } 
            } 
        }
 
        /// 
        /// Critical - calls critical code
        /// 
        [SecurityCritical] 
        private void RepositionSplitMatra(ref ShapingWorkspace currentRun, ushort matraOffset)
        { 
            // this is a split matra.  Get its components and move them 
            // to the appropriate positions
 
            // decompose the matra and move its glyphs to the right spots
            char [] matraComponents =
                _charConverter.ToDecompositionList(currentRun.GetChar((ushort)(_firstCharIx + matraOffset)));
            if (matraComponents != null) 
            {
                ushort componentIx = 0; 
                // save the positioning info from this new char shape... 
                IndicRepositioningClass nextComponentRepositioningClass =
                        _charConverter.ToRepositioningClass(matraComponents[componentIx]); 

                while (componentIx <  matraComponents.Length)
                {
                    // go through the rest of the components till we find the next one that 
                    // repositions to a different place...
                    IndicRepositioningClass componentRepositioningClass = nextComponentRepositioningClass; 
                    ushort componentsToMove = 1; 
                    ushort toPosition =  GetRepositionOffsetAndUpdateCluster(componentRepositioningClass,
                                                                    false,  // not reph 
                                                                    0);

                    while (++componentIx < matraComponents.Length)
                    { 
                        nextComponentRepositioningClass =
                                _charConverter.ToRepositioningClass(matraComponents[componentIx]); 
                        _unmappedGlyphsCount += 1;         // keep track of number of added component glyphs 
                        if (nextComponentRepositioningClass == componentRepositioningClass)
                        { 
                            ++componentsToMove;
                            continue;
                        }
 
                        break;
                    } 
 
                    RepositionCharacters(ref currentRun, matraOffset, toPosition,componentsToMove);
 
                    // re-call with char count to update the cluster.
                    GetRepositionOffsetAndUpdateCluster(componentRepositioningClass,
                                                        false,  // not reph
                                                        componentsToMove); 
                    matraOffset += componentsToMove;    // this will be our next starting "virtual" character offset
                } 
 
                _lastMatraRepositioningClass = nextComponentRepositioningClass;
                _isSplitMatraPresent = true; 

            }
            Debug.Assert(matraComponents != null, "ERROR - no split matra components array.");
        } 

        ///  
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        public bool AddCharToCluster (ref ShapingWorkspace currentRun)
        {
            CharShapeInfo charShapeFlags = currentRun.CurrentShape;
            bool isClusterOk = true; 

            // before processing the current character, check if its time to process the 
            // preceding cluster... 
            if ((charShapeFlags & CharShapeInfo.IsStartOfCluster) != 0 && _clusterSize > 0)
            { 
                ApplyClusterFeatures( ref currentRun );
                ResetClusterInfo(currentRun.CurrentCharIx);
            }
 
            if ((charShapeFlags & CharShapeInfo.RequiresInsertedBase) !=
                                                        CharShapeInfo.RequiresInsertedBase) 
            { 
                ushort clusterCharOffset = _clusterSize;
                IndicCharClass charClass = 
                    (IndicCharClass)(charShapeFlags & IndicShape.IndicCharClassMask);

                switch (charClass)
                { 
                    case IndicCharClass.Vowel:
                        if (_isVowelPresent) 
                        { 
                            // can't have two vowels per cluster
                            isClusterOk = false; 
                        }
                        else
                        {
                            // got a vowel this cluster 
                            _isVowelPresent = true;
                            _vowelOffset = clusterCharOffset; 
                            isClusterOk = AppendConsonant(ref currentRun, clusterCharOffset ); 
                        };
                        break; 
                    case IndicCharClass.NuktaConsonant:
                        isClusterOk = AppendConsonant(ref currentRun, clusterCharOffset );
                        if (isClusterOk)
                        { 
                            _isNuktaPresent = true;
                            _lastNuktaOffset = clusterCharOffset; 
                        } 
                        break;
                    case IndicCharClass.Ra: 
                    case IndicCharClass.Consonant:
                        isClusterOk = AppendConsonant(ref currentRun, clusterCharOffset );
                        break;
                    case IndicCharClass.ZWJ: 
                        isClusterOk = AppendZWJ(ref currentRun, clusterCharOffset  );
                        break; 
                    case IndicCharClass.ZWNJ: 
                        isClusterOk = AppendZWNJ(ref currentRun, clusterCharOffset  );
                        break; 
                    case IndicCharClass.Halant:
                        isClusterOk = AppendHalant( );
                        if (isClusterOk)
                        { 
                            _lastHalantOffset = clusterCharOffset;
                        } 
                        break; 
                    case IndicCharClass.Nukta:
                        isClusterOk = AppendNukta( ref currentRun, clusterCharOffset ); 
                        if (isClusterOk)
                        {
                            _lastNuktaOffset = clusterCharOffset; // keep track of latest nukta
                        } 
                        break;
 
                    case IndicCharClass.Vedic: 
                    case IndicCharClass.VowelSign:
                        isClusterOk = AppendVowelSign( ref currentRun, clusterCharOffset ); 
                        break;

                    case IndicCharClass.Matra:
                        isClusterOk = AppendMatra( ref currentRun, clusterCharOffset ); 
                        break;
                    case IndicCharClass.NBSP: 
                        break; 
                    default:
                        break; 
                }

                if (!isClusterOk)
                { 
                    // process the cluster up till now
                    Debug.Assert(_clusterSize > 0,"Zero length cluster is illegal"); 
                    ApplyClusterFeatures( ref currentRun ); 
                    ResetClusterInfo ( currentRun.CurrentCharIx );
                } 
            }

            ++_clusterSize;     // this character is part of the cluster
            return isClusterOk; 

        } 
 
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical]
        private bool AppendConsonant ( ref ShapingWorkspace currentRun, ushort consonantCharOffset )
        { 

            _isHalantActive = false; 
 
//            if ( _consonantState == ConsonantState.EndOfClusterReached )
//            { 
//                return false;
//            }

            IndicFormFlags consonantFormFlags = _charConverter.ToFormInfo(currentRun.CurrentChar); 

            // Check if we've already discovered a prebase ra (a rare bird!), and 
            // if so... 
            if (!_useV2FontRules)
            { 
                if (_postbaseRaOffset > 0 &&
                ((consonantFormFlags & IndicFormFlags.PostForm) == 0 ||
                 (consonantFormFlags & IndicFormFlags.PostbaseReph) != 0))
                { 
                    // The old-style Kartika font expects that post-base reordering ra to
                    // have no non-post-base forms after it.  Also, a second post-base ra will 
                    // supercede the first (which means the first is not a post-base form). 
                    // If either of these are true, then set our state to consAfterMain and
                    // clear the prebase ra offset. 
                    // <--
                    _lastMainOffset = _lastConsonantOffset;
                    _consonantState = ConsonantState.AfterMain;
                    _postbaseRaOffset = 0; 
                }
 
            } 

            switch (_consonantState) 
            {
                case ConsonantState.AfterReph:
                    _isRephReorderPending = true;
                    goto FirstMainConsonant; 

                case ConsonantState.AfterZWJ: 
                    goto FirstMainConsonant; 

                case ConsonantState.Start: 
                    if ((consonantFormFlags & IndicFormFlags.RephForm) != 0)
                    {
                        _consonantState = ConsonantState.AfterReph;
                        return true; 
                    }
 
FirstMainConsonant: 
                    // normal handling of the first main consonant (ie, not
                    // a reph).  Also come through here for ZWJ (which is 
                    // why we check whether _halfOffset has already been
                    // set)
                    if ((consonantFormFlags & IndicFormFlags.HalfForm) != 0  &&
                         _halfOffset > consonantCharOffset) 
                    {
                        _halfOffset = consonantCharOffset; 
                    } 

                    _consonantState = (consonantFormFlags & IndicFormFlags.DravidianBase) != 0 ? 
                        ConsonantState.AfterDravidianBase : ConsonantState.AfterMain;

                    _mainOffset = consonantCharOffset;
                    break; 
                case ConsonantState.AfterDravidianBase:
                    // for dravidian base chars (malayalam script only) on old behavior 
                    // fonts, don't shape second consonant 
                    consonantFormFlags = 0; // consonant may not be sub/post after dravidian base
                    goto AfterMain; 

                case ConsonantState.AfterMain:
AfterMain:
                    if ((consonantFormFlags & 
                        (IndicFormFlags.PostForm | IndicFormFlags.SubForm | IndicFormFlags.VattuForm)) != 0)
                    { 
                        // if this is Kannada text and there's a zero width joiner since the 
                        // halant, swap the two...
                        if ( _isZWJPresent && _scriptIx == IndicScriptIndices.Kannada ) 
                        {
                            if ( _ZWJOffset == (ushort)(_lastHalantOffset + 1) )
                            {
                                // For compatibility with legacy useage in Kannada, 
                                // Ra+h+ZWJ must behave like Ra+ZWJ+h.  So always
                                // order these as Ra+ZWJ+h (and, of course, the ZWJ+h 
                                // belong with the following sub.. 
                                ushort firstGlyphIx = currentRun.GetGlyphIx(_firstCharIx);
                                ushort halantGlyphIx = (ushort)(firstGlyphIx + _lastHalantOffset); 
                                ushort zwGlyphIx =  (ushort)(firstGlyphIx + _ZWJOffset);
                                ushort halantFlags = currentRun.GlyphInfoList.GlyphFlags[halantGlyphIx];

                                currentRun.GlyphInfoList.Glyphs[halantGlyphIx] = 
                                                    currentRun.GlyphInfoList.Glyphs[zwGlyphIx];
                                currentRun.GlyphInfoList.GlyphFlags[halantGlyphIx] = 
                                                    currentRun.GlyphInfoList.GlyphFlags[zwGlyphIx]; 

                                currentRun.GlyphInfoList.Glyphs[zwGlyphIx] = 
                                                                    _fontClient.HalantGlyph;
                                currentRun.GlyphInfoList.GlyphFlags[zwGlyphIx] = halantFlags;

                                _ZWJOffset = _lastHalantOffset; 
                            }
                            else if ( (ushort)(_ZWJOffset + 1) == _lastHalantOffset) 
                            { 
                                _lastHalantOffset = _ZWJOffset;
                            } 
                        }

                        if ((consonantFormFlags & IndicFormFlags.PostForm) != 0)
                        { 
                            _postOffset = _lastHalantOffset;
                            _consonantState = ConsonantState.AfterPost; 
                        } 
                        else
                        { 
                            _subOffset = _lastHalantOffset;
                            _consonantState = ConsonantState.AfterSub;
                        }
                    } 
                    else
                    { 
                        if (_halfOffset == _mainOffset && _mainOffset >= _lastMainOffset) 
                        {
                            // this is the second consonant (or maybe third, if 
                            // there's a reph) and the first was a half form
                            // so set this one as our main.
                           _mainOffset =  consonantCharOffset;
                        } 

                        // main is now HERE //* check for NoConjuncts? 
                        _lastMainOffset = consonantCharOffset; 
                    }
 
                    break;
                case ConsonantState.AfterSub:
                    if ((consonantFormFlags & IndicFormFlags.PostForm) != 0)
                    { 
                        _postOffset = _lastHalantOffset;
                        _consonantState = ConsonantState.AfterPost; 
                    } 
                    else if ((consonantFormFlags & (IndicFormFlags.SubForm | IndicFormFlags.VattuForm)) == 0)
                    { 
                        // a consonant with no shape class following a sub-base consonant.
                        // Hmmm!  Check a little further...

                        // The rules are that a prebase ra can come after a sub-form. 
                        // So if this is a prebase ra AND we haven't already marked an
                        // earlier ra as a prebase form, then we'll let this one past. 
                        // However, in all other cases, roll back our state... 
                        if ((consonantFormFlags & IndicFormFlags.PostbaseReph) == 0 || _postbaseRaOffset > 0)
                        { 
                            // roll everything back to main and set this as the last main,
                            // and hope font forms a conjunct.
                            _lastMainOffset = consonantCharOffset;
                            _subOffset = _postbaseRaOffset = 0; 
                            _consonantState = ConsonantState.AfterMain;
                        } 
                    } 
                    break;
                case ConsonantState.AfterPost: 
                    if ((consonantFormFlags & IndicFormFlags.PostForm) == 0)
                    {
                        // hmmm!  This isn't a post-form consonant.  Better check further
                        // (have to do something about this!) 

                        // The rules are that a prebase ra can come after a post-form. 
                        // So if this is a prebase ra AND we haven't already marked an 
                        // earlier ra as a prebase form, then we'll let this one past.
                        // However, in all other cases, roll back our state... 
                        if ((consonantFormFlags & IndicFormFlags.PostbaseReph) == 0 || _postbaseRaOffset > 0)
                        {
                            if ((consonantFormFlags & (IndicFormFlags.SubForm | IndicFormFlags.VattuForm)) != 0)
                            { 
                                // roll everything back to subs
                                // and hope font forms a conjunct. 
                                _lastMainOffset = _lastConsonantOffset; 
                                _subOffset = _lastHalantOffset;
                                _consonantState = ConsonantState.AfterSub; 
                            }
                            else
                            {
                                // a consonant with no shape class (maybe font'll do something 
                                // with it?); so roll everything back to main and set this
                                // as the last main, and hope for conjunct formations 
                                _lastMainOffset = consonantCharOffset; 
                                _consonantState = ConsonantState.AfterMain;
                            } 
                            _postOffset = _postbaseRaOffset = 0;   // clear. If appropriate, raoffset'll be reset below
                        }

                    } 
                    break;
            } 
 
            if (_mainOffset != consonantCharOffset)
            { 
                // take care of vattus.  If this consonant is not the reph or 1st
                // main consonant, check for vattu form
                if ((consonantFormFlags & IndicFormFlags.VattuForm) != 0)
                { 
                    _isVattuPresent = true;
                } 
 
                // -->
                // Special reordering ra handling... 
                if ((consonantFormFlags & IndicFormFlags.PostbaseReph) != 0 && _postbaseRaOffset == 0)
                {
                    // post-base RA glyph will get reordered to before the
                    // last main consonant.  So set the offset if this is a potential post main RA 
                    _isReorderingNecessary = true;
                    _postbaseRaOffset = _lastHalantOffset; 
                } 
                // <--
                // End special reordering ra handling... 
            }

            _lastConsonantOffset = consonantCharOffset;
            return true; 
        }
 
        private bool AppendHalant () 
        {
            bool succeeded = false; 

            if ( _isMatraPresent )
            {
                // This is a post-matra halant.  Check that there's not 
                // already one.
                if (!_isPostMatraHalantPresent) 
                { 
                    succeeded = _isPostMatraHalantPresent = true;
                } 
            }
            else if (!_isHalantActive)
            {
                // this is the first (only) halant since the last consonant (good!) 
                succeeded = _isHalantActive = true;
            } 
 
            return succeeded;
        } 

        /// 
        /// Critical - calls critical code
        ///  
        [SecurityCritical]
        private bool AppendNukta ( ref ShapingWorkspace currentRun, ushort nuktaCharOffset ) 
        { 
            bool succeeded = false;
            if ( _isMatraPresent ) 
            {
                // this nukta follows a matra, so add the matra's
                // positioning info to our shape info
                if (!_isPostMatraNuktaPresent) 
                {
                    // move this nukta to after the preceding (reordered) matra 
                    // reposition this matra 
                    IndicRepositioningClass nuktaReposClass = _lastMatraRepositioningClass;
                    ushort toPosition = GetRepositionOffsetAndUpdateCluster(nuktaReposClass, 
                                                                            false,  // not reph
                                                                            1);
                    RepositionCharacters(ref currentRun,
                                         (ushort)(nuktaCharOffset + _unmappedGlyphsCount), 
                                         toPosition,
                                         1); 
 
                    succeeded = _isPostMatraNuktaPresent = true;
                    _lastNuktaOffset = toPosition; 
                }


            } 
                // won't allow multiple nuktas for one consonant (ok, no more than two)
            else if (_lastNuktaOffset <= _lastConsonantOffset + 1) 
            { 
                // we may have to adjust the consonant state..
                if (_consonantState == ConsonantState.AfterReph) 
                {
                    // if this nukta is modifying what we thought was our
                    // reph, better change that
                    _isRephReorderPending = false; 
                    _mainOffset = _lastConsonantOffset;
                    _consonantState = ConsonantState.AfterMain; 
                } 
                else if (_consonantState == ConsonantState.AfterSub || _consonantState == ConsonantState.AfterPost)
                { 
                    // nukta not allowed on subs/posts
                    // roll back to main
                    _lastMainOffset = _lastConsonantOffset;
                    _consonantState = ConsonantState.AfterMain; 
                }
 
                succeeded = _isNuktaPresent = true; 
            }
 
            return succeeded;
        }

        private bool AppendZWJ ( ref ShapingWorkspace currentRun, ushort zwjCharOffset ) 
        {
            if (_isHalantActive) 
            { 
                if (_consonantState == ConsonantState.AfterReph &&
                    _scriptIx == IndicScriptIndices.Kannada) 
                {
                     // For compatibility with legacy useage in Kannada,
                     // Ra+h+ZWJ must behave like Ra+ZWJ+h...
                    _mainOffset = _lastConsonantOffset; 
                    _consonantState = ConsonantState.AfterMain;
                } 
                else 
                {
                    // this ZWJ follows a halant.  We expect that the following 
                    // consonant will be the main
                    _isHalantActive = false;
                    _isVattuPresent = false;
 
                    // treat this "C + H + ZWJ" as a half-form.
                    // (Malayalam has no half forms, but it does use this sequence 
                    // to specify the chillaksaram forms and we may want any pre-main 
                    // matras to go behind the chillaksaram (in ReorderGlyphs())
                    if (_halfOffset > _mainOffset) 
                    {
                        _halfOffset = _mainOffset;
                    }
 
                    _mainOffset = (ushort)(zwjCharOffset + 1);
                    _consonantState = ConsonantState.AfterZWJ; 
                } 

                // if this ZWJ is followin a RA+h that we thought might be 
                // a prebase form, now we know it isn't!
                if (_postbaseRaOffset == _lastConsonantOffset)
                {
                    _postbaseRaOffset = 0; 
                }
 
            } 
            else if ( _consonantState == ConsonantState.AfterReph)
            { 
               _mainOffset = _lastConsonantOffset;
               _consonantState = ConsonantState.AfterMain;
            }
 
            if (!_isZWJPresent)
            { 
                _isZWJPresent = true; 
                _ZWJOffset = zwjCharOffset;
            } 
            return true;
        }

        private bool AppendZWNJ (   ref ShapingWorkspace currentRun, ushort zwnjCharOffset ) 
        {
            if (_isHalantActive) 
            { 
                // ZWNJ following a C+H.
                _isHalantActive = false; 

                if (_postbaseRaOffset == 0 || _postbaseRaOffset < _lastConsonantOffset)
                {
                    _mainOffset = _lastConsonantOffset; 
                    _postbaseRaOffset = 0;
                } 
                else 
                {
                    // last consonant is our pre-base ra.  Mark it as "last main", too. 
                    _lastMainOffset = _lastConsonantOffset;
                }
            }
            else if (_scriptIx == IndicScriptIndices.Bengali) 
            {
                // ZWNJ following a ra (only for Bengali) 
                _mainOffset = _lastConsonantOffset; 
                _consonantState = ConsonantState.AfterMain;
            } 

            _isZWJPresent = true;
            _ZWJOffset = zwnjCharOffset;
            return true; 
        }
 
        ///  
        /// Critical - calls critical code
        ///  
        [SecurityCritical]
        private bool AppendMatra ( ref ShapingWorkspace currentRun, ushort matraCharOffset  )
        {
            bool succeeded = false; // assume the worst! 
            char matraChar = currentRun.CurrentChar;
            IndicFormFlags matraPositionFlags = _charConverter.ToFormInfo(matraChar); 
 
                // check that this matra has no positions already claimed by preceding matras
            if (matraPositionFlags > _matraPositionFlags) 
            {
                // keep track of first matra
                if (_matraPositionFlags == 0)
                { 
                    FinalizeOffsets(ref currentRun, matraCharOffset, false);
                } 
 
                // if this is a post-vowel matra, validate the matra
                if (_isVowelPresent && _vowelOffset == _lastConsonantOffset) 
                {
                    char[] invalidVowelList =
                        _charConverter.GetMatraInvalidVowelList(matraChar);
 
                    if (invalidVowelList != null)
                    { 
                        char vowelChar = currentRun.GetChar((ushort)(_firstCharIx + _vowelOffset)); 
                        for (int i = 0; i < invalidVowelList.Length; ++i)
                        { 
                            if (invalidVowelList[i] == vowelChar)
                            {
                                return false;   // this matra is not allowed with this vowel base
                            } 
                        }
                    } 
                } 

                _matraPositionFlags |= matraPositionFlags; 
                succeeded = true;
            }
            else
            { 
                // Seems this matra wants to position in the same place as a previous matra.
                // This is not allowed, except for a couple of Kannada exceptions 
                // (0xCCA+0xCD5 or 0xCC6+0xCC2+0xCD5) 
                if (matraChar == '\u0CD5')
                { 
                    // for Kannada script, there are three ways to type the oo vowel sign
                    // if this is one of those, we're happy (this is the only acceptable
                    // case of two matras with the same reordering position)
                    char prevChar = currentRun.PreviousChar; 
                    if (prevChar == '\u0CCA')
                    { 
                        succeeded = true; 
                    }
                    else if (prevChar == '\u0CC2') 
                    {
                        ushort matraCharIx = currentRun.CurrentCharIx;
                        if (matraCharIx >= _firstCharIx + 3 &&
                             currentRun.GetChar((ushort)(matraCharIx - 2)) == '\u0CC6') 
                        {
                            succeeded = true; 
                        } 
                    }
 
                }
            }

            if (succeeded) 
            {
                CharShapeInfo matraShapeInfo = _charConverter.ToShapeInfo(matraChar); 
                bool isSplitMatra = 
                    ((matraShapeInfo & IndicShape.ShapeIncludesPositioningInfoFlag) == 0);
                if (isSplitMatra) 
                {
                    // This is a split matra...so move the glyphs where they
                    // belong
                    RepositionSplitMatra(ref currentRun, matraCharOffset ); 
                }
                else 
                { 
                    // reposition this matra
                    _lastMatraRepositioningClass = GetRepositioningClass(matraShapeInfo); 
                    ushort toPosition = GetRepositionOffsetAndUpdateCluster(_lastMatraRepositioningClass,
                                                                            false,  // not reph
                                                                            1);
                    RepositionCharacters(ref currentRun, (ushort)(matraCharOffset + _unmappedGlyphsCount), toPosition, 1); 

                    // special case for Gurmukhi - when there's no post consonant but 
                    // there is an after-post matra, then move the reph after the 
                    // matra (unless its one of the two matras,  0xa3e or 0xa40)
                    if (_isRephReorderPending && _scriptIx == IndicScriptIndices.Gurmukhi) 
                    {
                        if (_lastMatraRepositioningClass == IndicRepositioningClass.AfterPost)
                        {
                            if (matraChar != '\u0a3e' && matraChar != '\u0a40') 
                            {
                               _gurmukhiRephGoesAfterPostMatra = true; 
                            } 
                        }
                    } 
                }
                _isMatraPresent = true;

            } 
            return succeeded;
        } 
 
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical]
        private bool AppendVowelSign ( ref ShapingWorkspace currentRun, ushort vowelSignCharOffset  )
        { 
            bool succeeded = true;
            FinalizeOffsets( ref currentRun, vowelSignCharOffset, false ); 
 
            // a vedic is a vowel sign and is the only way we'd see multiple
            // vowel signs.  We can only have one of each vedic and only 
            // two vedics, so check if preceding character is same as this one
            // (that would be bad)
            char vowelSignChar = currentRun.CurrentChar;
            succeeded = (currentRun.PreviousChar != vowelSignChar); 
            if (succeeded)
            { 
                // for matras and vowel signs, reposition them now 
                IndicRepositioningClass signReposClass = _charConverter.ToRepositioningClass(vowelSignChar);
                ushort toPosition = GetRepositionOffsetAndUpdateCluster(signReposClass, 
                                                                        false,  // not reph
                                                                        1);
                RepositionCharacters(ref currentRun,
                                     (ushort)(vowelSignCharOffset + _unmappedGlyphsCount), 
                                     toPosition,
                                     1); 
            } 
            return succeeded || vowelSignCharOffset == 0;
        } 


        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        public void FinalizeOffsets ( ref ShapingWorkspace currentRun, ushort currentCharOffset, bool isFinalCall ) 
        {
            if (_lastMainOffset < _mainOffset) 
            {
                _lastMainOffset = _mainOffset;
            }
 
            if (currentCharOffset > 0)
            { 
 
               if (_consonantState != ConsonantState.EndOfClusterReached &&
                   _consonantState != ConsonantState.Start) 
                {
                    // if there's no half forms, set the half offset now to the first
                    // consonant (in case any matra wants to reposition to After Start)
                    if (_halfOffset > _mainOffset) 
                    {
                        _halfOffset = _mainOffset; 
                    } 

                    if ( !_useV2FontRules ) 
                    {

                        if ( _lastMainOffset + 2 < currentCharOffset)
                        { 
                            // for non-V2 script fonts (ie, pre-Vista Indic fonts) we
                            // need to effectively swap the post-main consonants and their 
                            // halants before applying the font features because these 
                            // fonts use C+h lookups rather than h+C lookups.
 
                            // find first halant
                            ushort halantOffset = (ushort)(_lastMainOffset + 1); // start here
                            ushort halantGlyphIx =
                                (ushort)(currentRun.GetGlyphIx(_firstCharIx) + halantOffset); 
                            bool foundHalant =
                                (_fontClient.HalantGlyph == currentRun.GetGlyph(halantGlyphIx)); 
                            if (!foundHalant && (_lastMainOffset + 3 < _clusterSize)) 
                            {
                                // didn't find a halant.  Maybe this is a ZWJ?  Try the next glyph 
                                foundHalant = (_fontClient.HalantGlyph == currentRun.GetGlyph(++halantGlyphIx));
                                ++halantOffset;
                            }
                            if (foundHalant) 
                            {
                                // so, we have our halant.  If there's a trailing halant, 
                                // move the first halant based on its repositioning class, 
                                // otherwise if there's a sub- or post- consonant, move
                                // past it 
                                ushort toPosition = halantOffset;
                                if (_isHalantActive)
                                {
                                     toPosition = 
                                        GetRepositionOffsetAndUpdateCluster(_charConverter.HalantRepositioningClass,
                                                                        false, 
                                                                        0); 
                                }
                                else if (_postOffset > 0 || _subOffset > 0) 
                                {
                                   toPosition = currentCharOffset;
                                }
 
                                RepositionCharacters(ref currentRun, halantOffset, toPosition, 1 );
 
                            } 

                        } 


                    }
 
                    _consonantState = ConsonantState.EndOfClusterReached;
                } 
 
                if (isFinalCall)
                { 
                    if (_isRephReorderPending)
                    {
                        // move the reph to where it belong...
                        IndicRepositioningClass rephPosition = 
                            _charConverter.ToRepositioningClass( currentRun.GetChar(_firstCharIx) );
                        ushort toPosition = GetRepositionOffsetAndUpdateCluster(rephPosition, 
                                                                                true,   // is reph 
                                                                                2);
 
                        RepositionCharacters(ref currentRun,0,toPosition,2);

                        if (_lastMainOffset == _mainOffset || (_postOffset > 0 && toPosition > _postOffset))
                        { 
                            _isRephReorderPending = false;
                        } 
                        else 
                        {
                            _isReorderingNecessary = true; 
                        }
                    }

                    if (_unmappedGlyphsCount > 0) 
                    {
                        UpdateSplitMatraClusterOffsets(ref currentRun); 
                    } 
                }
 
            }


        } 

        ///  
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        private void UpdateSplitMatraClusterOffsets(ref ShapingWorkspace currentRun)
        {
            // Split matra components mean that the char:glyph offset relationship
            // is not 1:1 for this cluster.  We have already moved all the matras 
            // to their places and need to make some adjustments before we try
            // to have OTLS apply the font features.  Our strategy will be to 
            // try to map 2 glyphs to 1 character till we've compensated for 
            // all the matra component glyphs added by...
            //      first, if there's a reph map it to one character 
            //      second, if there's a prebase ra, map it to one character
            //      third, if there's a sub form consonant, map the halant-consonant
            //              pair to one glyph
            //      fourth, do the same if there's a post form consonant 
            //      fifth, if more than one matra are adjacent to each other, map
            //              them to one glyph 
            //      sixth, if we've still not brought our "virtual" character 
            //              count back to par and we have a prebase matra, map
            //              the following glyph to the same character as the 
            //              matra
            //      finally, if all else fails map the remaining "extra" glyphs
            //              to the same character as the last glyph with a valid
            //              FirstChars value. 
            GlyphInfoList glyphs = currentRun.GlyphInfoList;
            UshortList charMap = currentRun.CharMap; 
            ushort firstGlyphInCluster = currentRun.GetGlyphIx(_firstCharIx); 
            ushort lastGlyphInCluster =
                (ushort)(firstGlyphInCluster + _clusterSize + _unmappedGlyphsCount - 1); 
            ushort lastCharInCluster =
                (ushort)(_firstCharIx + _clusterSize - 1);

            // first reset the first chars and charmap entries to 1:1 glyphIx to charIx 
            // when this is done, the extra glyphs will all refer to character indices that
            // are invalid (beyond the actual end of the character range).  Not to worry! 
            // We're gonna fix it up... 
            ushort nextGlyphIx = firstGlyphInCluster;
            ushort nextCharIx = _firstCharIx; 
            while (nextCharIx <= lastCharInCluster)
            {
                glyphs.FirstChars[nextGlyphIx] = nextCharIx;
                charMap[nextCharIx++] = nextGlyphIx++; 
            }
            while (nextGlyphIx <= lastGlyphInCluster) 
            { 
                glyphs.FirstChars[nextGlyphIx++] = nextCharIx++;
            } 

            // now look for possible chars to "squeeze out" so that our glyph to character
            // mapping gets in [....]
            ushort passId = 0; 
            while (_unmappedGlyphsCount > 0)
            { 
                bool isSqueezeTarget = false; 
                ushort clusterCharOffset = 0;
                switch(passId++) 
                {
                    case 0:
                        if (_isPostMatraNuktaPresent &&
                            _lastNuktaOffset > 0 && _lastNuktaOffset < _mainOffset) 
                        {
                            // compress the matra+nukta to one character 
                            clusterCharOffset = _preMainMatraOffset; 
                            _lastNuktaOffset = _preMainMatraOffset;
                            isSqueezeTarget = true; 
                        }
                        break;
                    case 1:
                        if (_postbaseRaOffset > 0) 
                        {
                            // compress the prebase ra to one character 
                            clusterCharOffset = _postbaseRaOffset; 
                            isSqueezeTarget = true;
 
                            if (_lastMainOffset == (ushort)(_postbaseRaOffset + 1))
                            {
                                _lastMainOffset = _postbaseRaOffset;
                            } 
                        }
                        break; 
                    case 2: 
                        if (_rephOffset > 0)
                        { 
                            // compress the reph to one character
                            clusterCharOffset = _rephOffset;
                            isSqueezeTarget = true;
                        } 
                        break;
                    case 3: 
                        if (_subOffset > 0 && _postbaseRaOffset != _subOffset) 
                        {
                            // compress the (first) sub-base to one character 
                            clusterCharOffset = _subOffset;
                            isSqueezeTarget = true;
                        }
                        break; 
                    case 4:
                        if (_postOffset > 0) 
                        { 
                            // compress the (first) post-base to one character
                            clusterCharOffset = _postOffset; 
                            isSqueezeTarget = true;
                        }
                        break;
                    case 5: 
                        if (_isPostMatraNuktaPresent && _lastNuktaOffset > _mainOffset)
                        { 
                            // compress the matra+nukta to one character 
                            clusterCharOffset = (ushort)(_lastNuktaOffset - 1);
                            isSqueezeTarget = true; 
                        }
                        break;
                    case 6:
                        if ( _lastMainOffset > _mainOffset ) 
                        {
                            // compress the first base with the first character that follows it 
                            // (could be nukta, halant, ZWJ) to one character 
                            clusterCharOffset = _mainOffset;
                            isSqueezeTarget = true; 
                        }
                        break;
                    default:
                        // If we're here none (or not enough) of the possible 2 glyphs to 1 character 
                        // mapping assignments has been enough.  So just map all of the extra glyphs
                        // to the end of the cluster.  Be careful to check if the reph is at the end; 
                        // if so, adjust the reph to be the last char 
                        bool isRephAtEnd = _rephOffset >= lastCharInCluster;
 
                        nextGlyphIx = lastGlyphInCluster;
                        while (nextGlyphIx > firstGlyphInCluster &&
                               glyphs.FirstChars[nextGlyphIx] >= lastCharInCluster)
                        { 
                            glyphs.FirstChars[nextGlyphIx--] = lastCharInCluster;
                            if (isRephAtEnd) 
                            { 
                                _rephOffset = lastCharInCluster--;
                                isRephAtEnd = false; 
                            }
                        }
                        _unmappedGlyphsCount = 0;
                        break; 

                } 
 
                if (isSqueezeTarget)
                { 
                    if (_lastMainOffset > 0 && _lastMainOffset > clusterCharOffset) --_lastMainOffset;
                    if (_subOffset > 0 && _subOffset > clusterCharOffset) --_subOffset;
                    if (_postOffset > 0 && _postOffset > clusterCharOffset) --_postOffset;
                    if (_rephOffset > 0 && _rephOffset > clusterCharOffset) --_rephOffset; 
                    if (_postbaseRaOffset > 0 && _postbaseRaOffset > clusterCharOffset) --_postbaseRaOffset;
 
                    // now update the glyph firstchars.  The second glyph must now point to the 
                    // same character as the first glyph, so update all the remaining entries
                    // (subtract one from the enntry) 
                    nextGlyphIx = (ushort)(firstGlyphInCluster + clusterCharOffset);
                    while (++nextGlyphIx <= lastGlyphInCluster)
                    {
                        glyphs.FirstChars[nextGlyphIx] -= 1; 
                    }
 
                    _unmappedGlyphsCount -= 1; 
                }
            } 

            // Now we need to fix up the charmap.  Some of the characters may map to two
            // glyphs.  Find them, starting from the beginning of the cluster.  For each
            // pair of glyphs mapped to one character, adjust all the following charmap 
            // entries (add one to charmap entry)
            nextCharIx = _firstCharIx; 
            nextGlyphIx = firstGlyphInCluster; 
            ushort charsInRun = currentRun.CharsCount;
            while (nextCharIx <= lastCharInCluster) 
            {
                if (nextGlyphIx < lastGlyphInCluster &&
                    glyphs.FirstChars[nextGlyphIx + 1] == glyphs.FirstChars[nextGlyphIx])
                { 
                    ushort charMapIx = nextCharIx;
                    while (++charMapIx <= lastCharInCluster) 
                    { 
                        charMap[charMapIx] += 1;
                    } 

                    ++nextGlyphIx;
                }
                ++nextCharIx; 
                ++nextGlyphIx;
            } 
 
        }
 
        /// 
        /// Critical - calls critical code
        /// 
        [SecurityCritical] 
        private bool FindLateRepositionAnchor(ref ShapingWorkspace currentRun)
        { 
            bool foundAnchor = false; 
            ushort newLastMainOffset = _lastMainOffset;
 
            if (_mainOffset < _lastMainOffset)
            {
                // check if conjunct formed
                ushort mainGlyph = currentRun.GetGlyphIx((ushort)(_firstCharIx + _mainOffset)); 
                ushort nextGlyphToTest = currentRun.GetGlyphIx((ushort)(_firstCharIx + _lastMainOffset));
 
                if (mainGlyph < nextGlyphToTest) 
                {
                    // conjunct didn't form.  If looking for matra anchor, break on first explicit 
                    // halant.  Otherwise, keep looking for an earlier halant (main might be
                    // C+h+C+h+C).
                    ushort halantGlyphIx = nextGlyphToTest;
                    ushort halantGlyph = _fontClient.HalantGlyph; 
                    while (--nextGlyphToTest > mainGlyph)
                    { 
                        if (halantGlyph == currentRun.GetGlyph(nextGlyphToTest)) 
                        {
                            // found it.  Set main offset to point to the consonant just past here. 
                            ushort halantCharIx = currentRun.GlyphInfoList.FirstChars[nextGlyphToTest];
                            halantGlyphIx = nextGlyphToTest;
                            newLastMainOffset = (ushort)(halantCharIx + 1 - _firstCharIx);
 
                            if (_isSplitMatraPresent)
                            { 
                                // it may be that we mapped the main+halant glyphs to one character 
                                // because of added split matra component glyphs.  We need to check
                                // and if this is the case we need to increment newLastMainOffset 
                                if (currentRun.GlyphInfoList.FirstChars[nextGlyphToTest - 1] == halantCharIx)
                                {
                                    ++newLastMainOffset;
                                } 
                            }
 
                            foundAnchor = true;     // 

                            if(_isMatraReorderPending || _postbaseRaOffset > 0) 
                            {
                                break;
                            }
                        } 
                    }
 
                    // not quite done yet.  If there's a ZWJ/NJ right after (either of) the halant 
                    // we need to move past this
                    if (foundAnchor) 
                    {

                        if (_isZWJPresent)
                        { 
                            if((currentRun.GlyphInfoList.GlyphFlags[halantGlyphIx] & (ushort)GlyphFlags.ZeroWidth)
                                                != 0) 
                            { 
                               ++newLastMainOffset;
                            } 
                        }

                        // Also check if font has set the proposed new anchor points as a mark
                        // (some font mark sub-base consonants this way).  If this is so, we 
                        // don't want to move anything here....
                        if ((currentRun.GlyphInfoList.GlyphFlags[currentRun.GetGlyphIx(newLastMainOffset)] 
                                    & (ushort)GlyphFlags.GlyphTypeMask) 
                                                == (ushort)GlyphFlags.Mark)
                        { 
                            foundAnchor = false;     //
                        }
                    }
 

                } 
 
            }
 
            if (!foundAnchor && _scriptIx == IndicScriptIndices.Malayalam &&
                _isMatraReorderPending &&
                _isZWJPresent)
            { 
                // special Mayalayam case - if we have a chillaksaram that has
                // not become part of a composite glyph, we want to set our 
                // anchor to after the chillaksaram 
                // 
                ushort mainCharOffset = _postbaseRaOffset > 0 ? 
                                     (ushort)(_postbaseRaOffset - 1) : _lastMainOffset;
                if (mainCharOffset < _clusterSize)
                {
                    ushort halfFormGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _halfOffset)); 
                    ushort nextGlyphToTest = currentRun.GetGlyphIx((ushort)(_firstCharIx + _mainOffset));
                    ushort mainGlyph = currentRun.GetGlyphIx((ushort)(_firstCharIx + mainCharOffset)); 
                    while (nextGlyphToTest <= mainGlyph) 
                    {
                        if (nextGlyphToTest != halfFormGlyphIx && 
                           (currentRun.GlyphInfoList.GlyphFlags[nextGlyphToTest] & (ushort)GlyphFlags.GlyphTypeMask)
                                        != (ushort)GlyphFlags.Mark)
                        {
                            foundAnchor = true;     // 
                            newLastMainOffset = (ushort)(currentRun.GlyphInfoList.FirstChars[nextGlyphToTest] - _firstCharIx);
                            break; 
                        } 
                        ++nextGlyphToTest;
                    } 
                }
            }
            _lastMainOffset = foundAnchor ? newLastMainOffset : _mainOffset;
            return foundAnchor; 
        }
 
        private static IndicRepositioningClass GetRepositioningClass(CharShapeInfo charShape) 
        {
            Debug.Assert((charShape & (CharShapeInfo) IndicCharClass.IncludesPositioningInfo) != 0,"\"Includes Positioning Info\" flag not set."); 
            charShape = (CharShapeInfo)((ushort)charShape >> IndicShape.RepositioningInfoShift);
            return (IndicRepositioningClass)charShape;
        }
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------- 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation, 2003
// 
//  File:      IndicShape.cs
// 
//  Contents:  Implementation of Indic shaping engine and its factory 
//
//  Created:   06-15-2005 Nick Beal (nbeal) 
//
//-----------------------------------------------------------------------

// #define VALIDATE_CLUSTER_PARAMETERS 

using System; 
using System.Security; 
using System.Security.Permissions;
using System.Diagnostics; 
using System.Collections;
using System.Globalization;
using System.Windows;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using MS.Internal.FontCache; 
using MS.Internal.FontFace; 
using MS.Internal.PresentationCore;
 

namespace MS.Internal.Shaping
{
 
    /// 
    /// IndicCharClass - enumeration of Indic classification 
    ///  
    internal enum IndicCharClass : ushort
    { 
        Halant,
        Vedic,
        VowelSign,
        Matra, 
        Ra,             // all these first character classes have repositioning information in their shape info
        Neutral, 
        NBSP, 
        Consonant,
        NuktaConsonant, 
        Nukta,
        Vowel,
        ZWJ,
        ZWNJ, 
        Reserved,     // Unknown class --
        NumberOfCharClasses,  // This needs to be < 16, or else the flags and re-positioning classes will need to shifted 
 
        CharClassMask = 0xF,    // mask off the positioning info flag (used instead of CharShapeInfo.ShaperClassMask)
        IncludesPositioningInfo = 0x10,         // set if classification table entry includes repositioning info 

        // Note that the bits represented by CharClassMask | IncludesPositioningInfo are the
        // same bits as in CharShapeInfo.ShaperClassMask.  This is necessary as the Indic char
        // classifier expects the CharShapeInfo for each Unicode Indic character to have this 
        // IndicCharCLass information in the ShaperClassMask range.
 
 
    };
 

    internal enum IndicRepositioningClass : ushort
    {
        AtStart, 
        BeforeMain,
        AfterMain,          // Repositions to right behind main consonant 
        BeforeSub,          // Repositions to in front of first sub-base consonant's halant 
        AfterSub,           // Repositions to right behind sub-base consonant
        BeforePost,         // Repositions to in front of first post-base consonant's halant 
        AfterPost,          // Repositions to right behind post-base consonant
        BeforeEnd,          // Repositions to end of cluster
        NumberOfRepositioningClasses,    // there're 7 repositioning class members
 
        RepositioningClassMask = 0x07,  // NumberOfRepositioningClasses must fit within this mask
//        IsMatra     = 0x8, // this bit is set if the character is a matra 
 
        // Note that the bits represented by
        // RepositioningClassMask | IsConsonant | IsMatra are the 
        // same bits as in CharShapeInfo.ShaperClassMask.  This is necessary as the Indic char
        // cluster processor expects the CharShapeInfo for each reordering Indic character to
        // have this IndicRepositioningClass information in the ShaperClassMask range.
 
        // for matra's and reph chars, the entry in the char classification table is a packed entity
        // e{IndicCharClass} + ( e{IndicRepositioningClass} << RepositioningClassShift).  So, this 
        // shift constant for normalizing the repositioning class info when it is stored 
        // in a CharShapeInfo entity.   When the char classification information is saved
        // to the ShaperWorkspace shapeinfo array, the IndicCharClass information is 
        // replaced with the repositioning information and the IsConsonant, IsMatra bits
        // are set as appropriate
        RepositioningClassShift = 5,
    }; 

    internal enum ConsonantState : byte 
    { 
        Start,
        AfterZWJ, 
        AfterReph,
        AfterMain,
        AfterSub,
        AfterPost, 
        AfterDravidianBase,
        EndOfClusterReached 
    } 

 
    enum IndicFormFlags : byte
    {
        InitForm      = 0x01,   // only used in Bengali to flag that this character may take init form
 
        // Consonant flags (These are generally font dependent and will be cached with the font
        // data). 
        PostbaseReph  = 0x02, 
        DravidianBase = 0x04,
        RephForm      = 0x08, 
        VattuForm     = 0x10,
        SubForm       = 0x20,
        PostForm      = 0x40,
        HalfForm      = 0x80, 

        // Matra Flags (these indicate glyph's position relative to the base; These are not font 
        // dependent but will be cached with the consonant form flags). 
        PreBaseMatra   = 0x02,     // matra modifier
        AboveBaseMatra = 0x04,     // matra modifier 
        BelowBaseMatra = 0x08,     // matra modifiers
        PostBaseMatra  = 0x10,     // matra modifier
    };
 

    ///  
    /// The Indic Shaping Engine - (shapes Indic text) 
    /// 
    ///  
    /// The IShaper and IShapingEngine interfaces are implemented to
    /// provide the shaping methods for Indic Scripts.
    /// There are four Indic private types defined/used in this class:
    /// 1.) IndicShapeFSM - this class manages the shape information 
    /// 2.) IndicClusterCop - this class manages the canonical ordering
    /// 3.) IndicFontClient - this class manages the font interface 
    /// 4.) IndicCharClassInfo - contains the char classification tables 
    ///
    internal class IndicShape : BaseShape 
    {

        /// 
        /// All required shaping Features (GSUB) 
        /// 
        private static readonly Feature[]   _indicSubstitutionFeatures    = 
        { 
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.PrebaseSubstitutions,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.AboveBaseSubstitutions,1), 
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.BelowBaseSubstitutions,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.PostbaseSubstitutions,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.HalantForms,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.ContextualAlternates,1), 
        };
 
        ///  
        /// All required shaping Features (GPOS)
        ///  
        private static readonly Feature[]   _indicPositioningFeatures    =
        {
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.Kerning,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.Distances,1), 
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.AboveBaseMarkPositioning,1),
            new Feature(0,ushort.MaxValue,(uint)FeatureTags.BelowBaseMarkPositioning,1), 
        }; 

 

        //
        // Indic Script is only supported scripts
        // 
        private static readonly ScriptTags[] _supportedScripts = new ScriptTags[]
            {   ScriptTags.Bengali, 
                ScriptTags.Devanagari, 
                ScriptTags.Gujarati,
                ScriptTags.Gurmukhi, 
                ScriptTags.Kannada,
                ScriptTags.Malayalam,
                ScriptTags.Oriya,
                ScriptTags.Tamil, 
                ScriptTags.Telugu   };
 
 
        /// 
        /// Constructor for the Indic Open Type Shaping Engine. 
        /// 
        internal IndicShape()
        {
            // Indic diacritics have width 
            _forceDiacriticsToZeroWidth = false;
        } 
 

        //-------------------------------------- 
        //
        //  Internal Methods
        //
        //-------------------------------------- 

#region Internal methods 
        ///  
        /// IndicShape.SupportedScripts -
        ///  IShapingEngine member override 
        /// 
        /// Our supported scripts (9 Indic scripts).
        public override ScriptTags[] SupportedScripts
        { 
            get
            { 
                return _supportedScripts; 
            }
        } 

        /// 
        /// IndicShape.GetCharClassifier - creates
        ///  
        /// 
        ///     This will normally be overridden by derived shapers. It is used in OnLoadFont 
        ///  
        protected override ShaperCharacterClassifier GetCharClassifier(ScriptTags scriptTag, GlyphTypeface fontFace)
        { 
            return new IndicCharClassifier (scriptTag, fontFace);
        }

        internal const CharShapeInfo IndicCharClassMask = (CharShapeInfo)IndicCharClass.CharClassMask; 
        internal const ushort RepositioningInfoShift = (ushort)IndicRepositioningClass.RepositioningClassShift;
        internal const CharShapeInfo ShapeIncludesPositioningInfoFlag = (CharShapeInfo) IndicCharClass.IncludesPositioningInfo; 
 

        private bool _forceSerializedOn = false; 

        /// 
        ///     IndicShape.GetGlyphs - Indic implementation of the GetGlyphs() helper function.
        ///  
        /// shaping currentRun
        /// Text item 
        /// number of glyphs 
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical]
        unsafe protected override int GetGlyphs ( ref ShapingWorkspace currentRun, Item item )
        { 

            ushort charsCount = 0; 
 
            RecordTraceEvent(MS.Utility.EventType.StartEvent, "Indic Init Start");
 
            // initialize the cluster cop/state machine
            IndicCharClassifier indicClassifier = (IndicCharClassifier)currentRun.CharConverter;
            IndicShapeFSM stateMachine = new IndicShapeFSM( indicClassifier, currentRun.HasLeadingJoin );
 
            currentRun.IsForceSerializedClusterOn = _forceSerializedOn;
 
            RecordTraceEvent(MS.Utility.EventType.EndEvent, "IndicShape Init End"); 
            RecordTraceEvent(MS.Utility.EventType.StartEvent, "IndicShape ShapeIndicText Start");
 
            // Shape and initialize the glyph list
            // process the char stream, creating shape info, applying features
            // as necessary
            char nextChar; 
            while ( currentRun.GetNextChar(out nextChar) )
            { 
                ++charsCount; 

                CharShapeInfo nextShape = stateMachine.StepToNextState( nextChar ); 

                // Check for special handling; might be a nukta that needs reordering or a matra
                // that needs decomposing or maybe a Tamil akhand has just been completed
                if ((nextShape & CharShapeInfo.RequiresSpecialHandling) != 0) 
                {
                    // special handling required 
 
                    nextShape ^= CharShapeInfo.RequiresSpecialHandling; // clear the special handling flag
                    IndicCharClass nextCharClass = (IndicCharClass)(nextShape & IndicCharClassMask); 
                    Debug.Assert (nextCharClass < IndicCharClass.NumberOfCharClasses, "invalid Indic char class index");
                    switch (nextCharClass)
                    {
                        case IndicCharClass.Nukta: 
                            // swap the nukta and its preceeding halant
                            ushort prevGlyph = currentRun.PreviousGlyph; // get halant glyph 
                            CharShapeInfo prevShape = currentRun.PreviousShape; 
                            currentRun.PreviousGlyph = indicClassifier.ToGlyph(nextChar);
                            currentRun.SetShapeInfo(currentRun.PreviousCharIx,nextShape); 
                            currentRun.SetGlyphPropertiesUsingGlyph(prevShape, prevGlyph);
                            continue;

                        case IndicCharClass.Matra: 
                            // decompose the matra...
                            char[] matraComponents = indicClassifier.ToDecompositionList(currentRun.CurrentChar); 
                            if (matraComponents != null) 
                            {
                                currentRun.AddGlyphs(matraComponents.Length - 1); // add space for the extra glyphs 

                                currentRun.SetGlyphPropertiesUsingChar(nextShape, matraComponents[0] );
                                currentRun.CurrentShape = CharShapeInfo.NoFlagsSet;
                                for (int i = 1; i < matraComponents.Length; ++i) 
                                {
                                    currentRun.SetGlyphProperties(currentRun.CharConverter.ToGlyph( matraComponents[i] ) ); 
                                } 
                                currentRun.CurrentShape = nextShape;
 
                                continue;
                            }
                            break;
 
                        case IndicCharClass.Halant:
                            // a Tamil akhand? 
                            if (!stateMachine.IsAkhandPending(ref currentRun)) 
                            {
                                // no akhand, so this is end of cluster 
                                stateMachine.ResetStateMachine();
                            }
                            break;
                    } 

                } 
 
                currentRun.SetGlyphPropertiesUsingChar(nextShape, nextChar);
            } 

            // now go through the chars/shapes and apply the features cluster by cluster...
            currentRun.Reset(0,0,charsCount);
            IndicClusterCop clusterCop = new IndicClusterCop( (IndicFontClient)currentRun.FontClient ); 
            ushort charsCount2 = 0;
            while (currentRun.ToNextChar()) 
            { 
                ++charsCount2;
 
                if (!clusterCop.AddCharToCluster(ref currentRun))
                {
                    // an unexpected end of cluster has occurred.  we need
                    // to create one or more new clusters... 
                    ushort currentCharIx = currentRun.CurrentCharIx;
 
                    // find the start of the next cluster 
                    ushort nextClusterStartIx = (ushort)(currentCharIx + 1);
                    while (nextClusterStartIx < currentRun.CharsCount && 
                           (currentRun.GetShapeInfo(nextClusterStartIx) & CharShapeInfo.IsStartOfCluster) == 0)
                    {
                        ++nextClusterStartIx;
                    } 

                    // re-cluster the newly "orphaned" characters from the end of this unexpectedly 
                    // done cluster 
                    int orphanedCharsCount = nextClusterStartIx - currentRun.CurrentCharIx;
 
                    while ( orphanedCharsCount-- > 0 )
                    {
                        stateMachine.ResetStateMachine();
                        CharShapeInfo nextShape = stateMachine.StepToNextState(  currentRun.CurrentChar ); 
                        if ((nextShape & CharShapeInfo.RequiresInsertedBase) == CharShapeInfo.RequiresInsertedBase)
                        { 
                            currentRun.UpdateCurrentGlyphProperties(nextShape); 
                            clusterCop.ApplyClusterFeatures(ref currentRun);
                            for (ushort i = currentRun.CharsCount; --i > currentRun.CurrentCharIx;) 
                            {
                                currentRun.CharMap[i]  += 1;
                            }
                        } 

                        if ( orphanedCharsCount > 0 ) 
                        { 
                            ++charsCount2;
                            currentRun.ToNextChar(); 
                        }

                    }
 
                }
 
            } 

            clusterCop.ApplyClusterFeatures(ref currentRun); 
            RecordTraceEvent(MS.Utility.EventType.EndEvent, "IndicShape ShapeIndicText End");

            return currentRun.GlyphsCount;    // we're done
 

        } 
 
        /// 
        ///    IndicShape.ApplySubstitutionFeatures - default implementation of the GetGlyphs() helper function. 
        /// 
        /// shaping currentRun
        /// Set of gsub features to be applied to the unicode run.
        /// result of applying features 
        /// 
        /// Critical - this method calls unsafe methods. 
        ///  
        [SecurityCritical]
        protected override OpenTypeLayoutResult ApplySubstitutionFeatures( 
                                                        ref ShapingWorkspace currentRun,
                                                        FeatureSet       featureSet  )
        {
            //  Apply the Indic Text Features from currentRun 
            ShaperFontClient fontClient = currentRun.FontClient;
            return fontClient.SubstituteGlyphs( 
                                ref currentRun, 
                               _indicSubstitutionFeatures,
                               _indicSubstitutionFeatures.Length ); 

        }

        ///  
        ///    IndicShape.ApplyPositioningFeatures - generic implementation of the GetGlyphPlacement helper.
        ///     This method goes through a list of glyphs and adds placement information. 
        ///  
        /// the wrapper for glyph advances, offset arrays
        /// metrics for all the positioning features 
        /// Set of gpos features to be applied to the unicode run.
        /// result of applying features
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        protected override OpenTypeLayoutResult ApplyPositioningFeatures( 
                ref PlacementWorkspace   placementInfo,
                ref LayoutMetrics   layoutMetrics, 
                FeatureSet          featureSet )
        {
            ShaperFontClient fontClient = placementInfo.FontClient;
            return fontClient.PositionGlyphs( 
                        ref placementInfo,
                        ref layoutMetrics, 
                        _indicPositioningFeatures,     // In: List of features to apply 
                        _indicPositioningFeatures.Length );
 
        }

        /// 
        /// IndicShape.OnLoadFont - IShapingEngine method override. 
        /// 
        ///  
        ///     This should normally be sufficient for most shapers - the only 
        ///     exceptions are those shapers that want to always return true
        ///  
        /// Script of interest.
        /// Font face being loaded
        /// the font client
        /// True if font supports script. 
        /// 
        /// Critical - This method reads into raw font table bits. 
        /// Safe     - This method doesn't expose any critical data. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public override bool OnLoadFont(
            ScriptTags          scriptTag,
            GlyphTypeface       fontFace,
            out object          shaperFontClient 
            )
        { 
            // create the font client for this font/script 
            IndicCharClassifier indicClassifier = new IndicCharClassifier(scriptTag, fontFace);
            IndicFontClient fontClient = new IndicFontClient( fontFace, 
                                                              indicClassifier );


            // if font is happy with this script, return true and give caller the ShaperFontClient 
            // to use in all method calls to us.
            if (fontClient.ValidateScriptTag(scriptTag)) 
            { 
                shaperFontClient = fontClient;
                return true; 
            }
            else
            {
                shaperFontClient = null; 
                return false;
            } 
        } 

    } 
#endregion


    ///  
    /// Class IndicShapeFSM:
    ///  The Indic state machine 
    ///  
    /// 
    /// This class implements the Indic state machine.  The GetNextShape() routine 
    /// is the most important method, returning the corresponding shape flags for each
    /// unicode char in the text stream.
    /// 
    internal struct IndicShapeFSM 
    {
 
        ushort _shapedCharCount; 

        IndicClusterState _currentState; 
        IndicClusterState _previousState;

        IndicCharClassifier _charConverter;
 
        ushort _clusterSize;
 
        public const ushort ClusterSizeLimit = (ushort)15;     // cluster size limit (big enough for any valid cluster) 
        public const ushort MaximumConsonantOffset = (ushort)10; // better be less than ClusterSizeLimit!
 

        char _previousChar;
        bool _isAkhandPending;
        char[][] _akhands; 

        public IndicShapeFSM (IndicCharClassifier charConverter, bool hasLeadingZWJ) 
        { 

            _previousState = _currentState = IndicClusterState.StartState; 
            if (hasLeadingZWJ)
            {
                _currentState = IndicClusterState.StartLinkedState;
            } 

             _shapedCharCount = 0; 
             _clusterSize = 0; 
             _charConverter = charConverter;
 
             _isAkhandPending = false;
             _previousChar = '\0';
             _akhands = (charConverter.ScriptIx == IndicScriptIndices.Tamil) ?
                    charConverter.TamilAkhands : null; 
        }
 
        internal void ResetStateMachine() 
        {
            _currentState = IndicClusterState.StartState; 
            _clusterSize = 0;
            _isAkhandPending = false;
        }
 
        /// 
        ///   IndicShapeFSM.StepToNextState - process the latest character. 
        ///  
        /// 
        ///   This routine steps the state machine to its next state 
        ///   based on the current state and the next char.
        /// 
        /// state flags for current character appropriate to new state
        internal CharShapeInfo StepToNextState (  char nextChar ) 
        {
 
            // get the cluster state entry from the cluster state table.  The char shape flags from this 
            // table form our starting place for
            CharShapeInfo nextCharShape = _charConverter.ToShapeInfo(nextChar); 
            IndicCharClass nextCharClass = (IndicCharClass)(nextCharShape & IndicShape.IndicCharClassMask);
            Debug.Assert (nextCharClass < IndicCharClass.NumberOfCharClasses, "invalid Indic char class index");
            ushort tableEntry =
                IndicClusterStateTable[(((int)_currentState - 1) << 4) + (int)nextCharClass];   // 16 entries per state. 

            // if the char shape info includes repositioning information, save it in the lower nibble of the 
            // returned char shape info (we'll need this later when we are preparing the cluster for feature 
            // application)...
            _previousState = _currentState;     // save current state. 
            _currentState = (IndicClusterState)(tableEntry & ClusterStateMask);

            // some special things to look for...
            bool isRaConsonant = false; 
            switch (nextCharClass)
            { 
                case IndicCharClass.Ra: 
                    nextCharShape = (CharShapeInfo) nextCharClass;
 
                    // If this is supposedly a reph, verify that it is, in fact, a reph
                    if (_currentState == IndicClusterState.RaState)
                    {
                        if ((_charConverter.ToFormInfo(nextChar) & IndicFormFlags.RephForm) == 0) 
                        {
                            isRaConsonant = true; 
                            _currentState = IndicClusterState.ConsonantState; 
                        }
                    } 

                    break;
                case IndicCharClass.Vedic:
                case IndicCharClass.VowelSign: 
                case IndicCharClass.Matra:
                    if ((nextCharShape & (CharShapeInfo) IndicCharClass.IncludesPositioningInfo) == 0) 
                    { 
                        // matras without positioning info (must be a multi-component matra)
                        // must be specially handled (for adding extra glyphs into glyph 
                        // run)
                        nextCharShape |= CharShapeInfo.RequiresSpecialHandling;
                    }
                    else 
                    {
                         nextCharShape = (CharShapeInfo) nextCharClass; 
                    } 

                    break; 
                case IndicCharClass.Halant:
                    nextCharShape = (CharShapeInfo) nextCharClass;
                    if ( _charConverter.ScriptIx == IndicScriptIndices.Tamil)
                    { 
                        // check for akhands...
                        if (_isAkhandPending) 
                        { 
                            ResetStateMachine(); // akhand + halant, this is end of cluster
                            _previousChar = nextChar; 
                            ++_shapedCharCount;
                            return nextCharShape;
                        }
                        else 
                        {
                            nextCharShape |= CharShapeInfo.RequiresSpecialHandling; 
                        } 
                    }
                    break; 
                case IndicCharClass.Nukta:
                     nextCharShape = (CharShapeInfo) nextCharClass;

                        // this is a nukta following a halant, we'll need to swap 'em. 
                    if (_previousState == IndicClusterState.HalantState ||
                        _previousState == IndicClusterState.RephState) 
                    { 
                        nextCharShape |= CharShapeInfo.RequiresSpecialHandling;
                    } 
                    break;

                default:
                    Debug.Assert((nextCharShape & (CharShapeInfo) IndicCharClass.IncludesPositioningInfo) == 0, 
                                 "This character class should not have positioning info in its char shape!");
                    break; 
            } 

            // whenever cluster size is larger than 10, we're gonna get nervous.  Make sure 
            // that any further characters aren't consonants and make sure that under no
            // circumstances will we allow a cluster size greater than 15.
            if ( (tableEntry & StartOfCluster) == 0)
            { 
                if (++_clusterSize > MaximumConsonantOffset)
                { 
                    bool isClusterSizeOk = true; 
                    switch(nextCharClass)
                    { 
                        case IndicCharClass.Ra:
                        case IndicCharClass.Consonant:
                        case IndicCharClass.NuktaConsonant:
                            isClusterSizeOk = false; 
                            break;
                        default: 
                            if (_clusterSize >= ClusterSizeLimit) 
                            {
                                isClusterSizeOk = false; 
                            }
                            break;
                    }
 
                    if (!isClusterSizeOk)
                    { 
                        // arbitrary limit on cluster size.  If its reached, start a new cluster 
                        ResetStateMachine();
                        tableEntry = 
                            IndicClusterStateTable[(((int)_currentState - 1) << 4) + (int)nextCharClass];   // 16 entries per state.
                        _currentState = isRaConsonant ? IndicClusterState.ConsonantState :
                                                    (IndicClusterState)(tableEntry & ClusterStateMask);
 
                        _clusterSize = 1;
                    } 
                } 
            }
            else 
            {
               _clusterSize = 1;
               _isAkhandPending = false;
            } 

            if ( (tableEntry & ClusterShapeFlagsMask) != 0 ) 
            { 
                nextCharShape |= (CharShapeInfo)(tableEntry & ClusterShapeFlagsMask);
            } 

            _previousChar = nextChar;
            ++_shapedCharCount;
            return nextCharShape; 
        }
 
 
        /// 
        ///   IndicShapeFSM.IsAkhandPending - check if akhand is done. 
        /// 
        /// 
        ///   This routine steps the state machine to its next state
        ///   based on the current state and the next char. 
        /// 
        /// true if preceding consonant is valid start of akhand 
        ///  
        /// Critical - calls critical code
        ///  
        [SecurityCritical]
        internal bool IsAkhandPending(ref ShapingWorkspace currentRun)
        {
            _isAkhandPending = false; 
            if (_clusterSize > 1)
            { 
                ushort currentIx = currentRun.CurrentCharIx; 
                for (int i = 0; i < _akhands.Length; ++i)
                { 
                    ushort nextCharIx = (ushort)(currentIx - 1);
                    char[] akhandCandidate = _akhands[i];
                    if (nextCharIx + akhandCandidate.Length <= currentRun.CharsCount)
                    { 
                        _isAkhandPending = true; // we hope!
                        for (int j = 0; j < akhandCandidate.Length; ++j) 
                        { 
                            if (currentRun.GetChar(nextCharIx) != akhandCandidate[j])
                            { 
                                _isAkhandPending = false;   // nope, not this one!
                                break;
                            }
                            ++nextCharIx; 
                        }
 
                        if (_isAkhandPending) 
                        {
                            // we've just started an akhand sequence! 
                            break;
                        }
                    }
                } 
            }
 
            return _isAkhandPending; 
        }
 
        /// 
        /// IndicClusterState - enumeration of Indic cluster states
        /// ordinal position
        ///  
        private enum IndicClusterState : byte
        { 
            NoChange, 
            StartState,
            StartLinkedState, 
            NBSPState,
            StandaloneState,
            VowelState,
            VowelZWJState, 
            ConsonantState,
            RaState, 
            HalantState, 
            RephState,
            VowelSignState, 
            VedicState,
            SecondVedicState,
            ZWJState,
            ZWJHalantState, 
            MatraState,
            FinalState, 
            NumberOfIndicClusterStates, 

            ClusterStateMask = 0x1f,        // enough bits for all the cluster states, must be 
                                            // <= CharShapeInfo.ShapeClassMask
        };

        private const ushort ClusterStateMask = (ushort) IndicClusterState.ClusterStateMask; 
        private const ushort ClusterShapeFlagsMask = (ushort) CharShapeInfo.ShapeFlagsMask;
 
        // These values are used to populate the state tables.  Each entry is a packed 
        // value with the next state in the lower byte, and the shape flags in the upper
        // byte.  The GetNextShape routine unpacks these entries. 
        // The "ToState" definitions generally mean that the current character is
        // a continuation of the current cluster and that only the state machine's state
        // is changing
        private const ushort InvalidBase  = (ushort)CharShapeInfo.RequiresInsertedBase; 
        private const ushort ZWControlChar = (ushort) CharShapeInfo.IsUnicodeLayoutControl;
        private const ushort StartOfCluster = (ushort) CharShapeInfo.IsStartOfCluster; 
        private const ushort NoChange  = (ushort )IndicClusterState.NoChange; 

        private const ushort ToStartState  = (ushort )IndicClusterState.StartState; 
        private const ushort ToStartLinkedState = (ushort )IndicClusterState.StartLinkedState;
        private const ushort StartCluster  = ToStartState | StartOfCluster;

        private const ushort ToNBSPState  = (ushort )IndicClusterState.NBSPState; 
        private const ushort StartNBSPCluster  = ToNBSPState | StartOfCluster;
 
        private const ushort ToVowelState  = (ushort )IndicClusterState.VowelState; 
        private const ushort StartVowelCluster  = ToVowelState | StartOfCluster;
 
        private const ushort ToConsonantState  = (ushort )IndicClusterState.ConsonantState;
        private const ushort StartConsonantCluster  = ToConsonantState | StartOfCluster;
        private const ushort ToRaState  = (ushort )IndicClusterState.RaState;
        private const ushort StartRephConsonantCluster = ToRaState | StartOfCluster; 

        // These state machine entries are used to respond to ZWJ/ZWNJ characters in the text stream 
        private const ushort ToZWJState  = (ushort )IndicClusterState.ZWJState; 
        private const ushort ToVowelZWJState  = (ushort )IndicClusterState.VowelZWJState;
        private const ushort ToStandaloneState  = (ushort )IndicClusterState.StandaloneState; 
        private const ushort StartStandaloneCluster  = ToStandaloneState + StartOfCluster;

        private const ushort ToZWJHalantState  = (ushort )IndicClusterState.ZWJHalantState;
        private const ushort ToMatraState  = (ushort )IndicClusterState.MatraState; 
        private const ushort ToHalantState  = (ushort )IndicClusterState.HalantState;
        private const ushort ToRephState  = (ushort )IndicClusterState.RephState; 
 
        private const ushort ToVedicState  = (ushort )IndicClusterState.VedicState;
        private const ushort To2ndVedicState  = (ushort )IndicClusterState.SecondVedicState; 
        private const ushort ToVowelSignState  = (ushort )IndicClusterState.VowelSignState;

        private const ushort ToFinalState  = (ushort )IndicClusterState.FinalState;
        private const ushort EndOfCluster  = ToStartState; 
//        private const ushort StartNextCluster  = StartOfCluster;
 
        // The syllable state machine state table.  For each current state 
        // there are 16 entries - one for each char class (see IndicCharClass, 14 classes) and
        // 2 extra for padding - so that the first entry for any current state can 
        // be found at "current state << 4"
        private static readonly ushort[] IndicClusterStateTable  = //new IndicSyllableState[]
        {
            // State Table Entry        Input Char Class 
            StartCluster | InvalidBase,// Halant    (mark)
            StartCluster | InvalidBase,// Vedic     (mark) 
            StartCluster | InvalidBase,// VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding

            // StartLinkedState                  Input Char Class
            ToZWJHalantState,          // Halant    (mark) 
            ToStartLinkedState,        // Vedic     (mark)
            ToStartLinkedState,        // VowelSign (mark) 
            ToMatraState,              // Matra     (mark) 
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            ToStartLinkedState,        // Nukta     (mark) 
            StartVowelCluster,         // Vowel     (base)
            ToZWJState,                // ZWJ 
            StartCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 

            // NBSPState                         Input Char Class
            EndOfCluster,              // Halant    (mark)
            EndOfCluster,              // Vedic     (mark) 
            EndOfCluster,              // VowelSign (mark)
            ToMatraState,              // Matra     (mark) 
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            EndOfCluster,              // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            ToStandaloneState,         // ZWJ
            EndOfCluster,              // ZWNJ 
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
            // Standalone                  Input Char Class
            EndOfCluster,              // Halant    (mark)
            EndOfCluster,              // Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign  (mark) 
            StartCluster | InvalidBase,// Matra      (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            EndOfCluster,              // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding 

            // VowelState                 Input Char Class 
            ToHalantState,             // Halant    (mark)
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark) 
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            ToVowelState,              // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            ToVowelZWJState,           // ZWJ
            ToVowelZWJState,           // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 
 
            // VowelZWJState                 Input Char Class
            ToHalantState,             // Halant    (mark) 
            StartCluster | InvalidBase,// Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark)
            ToMatraState,              // Matra     (mark)
            ToVowelState,              // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            ToVowelState,              // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
            // ConsonantState           Input Char Class 
            ToHalantState,             // Halant    (mark)
            ToVedicState,              // Vedic     (mark) 
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark)
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta -- 
            ToConsonantState,          // Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            ToZWJState,                // ZWJ
            ToConsonantState,          // ZWNJ
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 

            // RaState                 Input Char Class 
            ToRephState,               // Halant    (mark) 
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark) 
            ToMatraState,              // Matra     (mark)
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            ToRaState,                 // Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            ToZWJState,                // ZWJ 
            ToConsonantState | ZWControlChar, // ZWNJ
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding
 
            // HalantState              Input Char Class
            StartCluster | InvalidBase,// Halant    (mark) 
            ToVedicState,              // Vedic     (mark) 
            ToVowelSignState,          // VowelSign (mark)
            StartCluster | InvalidBase,// Matra     (mark) 
            ToConsonantState,          // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  --
            ToConsonantState,          // Consonant -- 
            ToConsonantState,          // Consonant with Nukta --
            ToHalantState,             // Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base) 
            ToZWJState,                // ZWJ
            EndOfCluster | ZWControlChar, // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding

            // RephState                Input Char Class 
            StartCluster | InvalidBase,// Halant    (mark)
            ToVedicState,              // Vedic     (mark) 
            ToVowelSignState,          // VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark)
            ToConsonantState,          // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  --
            ToConsonantState,          // Consonant --
            ToConsonantState,          // Consonant with Nukta -- 
            ToRephState,               // Nukta     (mark)
            ToVowelState,              // Vowel     (base) 
            ToZWJState,                // ZWJ 
            EndOfCluster | ZWControlChar, // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding

            // VowelSignState                Input Char Class
            StartCluster | InvalidBase,// Halant    (mark) 
            ToVedicState,              // Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark) 
            StartRephConsonantCluster, // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            EndOfCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 

            // VedicState                Input Char Class
            StartCluster | InvalidBase,// Halant    (mark)
            To2ndVedicState,           // Vedic     (mark) 
            StartCluster | InvalidBase,// VowelSign (mark)
            StartCluster | InvalidBase,// Matra     (mark) 
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base) 
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            EndOfCluster,              // ZWNJ 
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
            // SecondVedicState                Input Char Class
            StartCluster | InvalidBase,  // Halant    (mark)
            EndOfCluster,              // Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark) 
            StartCluster | InvalidBase,// Matra     (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  --
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle) 
            EndOfCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding 

            // ZWState                Input Char Class 
            ToZWJHalantState,          // Halant    (mark)
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark) 
            ToConsonantState,          // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  -- 
            ToConsonantState,          // Consonant --
            ToConsonantState,          // Consonant with Nukta -- 
            ToZWJState,                // Nukta     (mark)
            ToVowelState,              // Vowel     (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 
 

            // ZWHalant                Input Char Class 
            StartCluster | InvalidBase,// Halant    (mark)
            StartCluster | InvalidBase,// Vedic     (mark)
            StartCluster | InvalidBase,// VowelSign (mark)
            StartCluster | InvalidBase,// Matra     (mark) 
            ToConsonantState,          // Ra --
            StartCluster,              // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,          // NBSP  -- 
            ToConsonantState,          // Consonant --
            ToConsonantState,          // Consonant with Nukta -- 
            StartCluster | InvalidBase,// Nukta     (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ 
            StartCluster,              // Unknown class --
            NoChange,NoChange,         // padding 
 
            // MatraState                Input Char Class
            ToMatraState,              // Halant    (mark) 
            ToVedicState,              // Vedic     (mark)
            ToVowelSignState,          // VowelSign (mark)
            ToMatraState,              // Matra     (mark)
            StartRephConsonantCluster, // Ra -- 
            StartCluster,              // Neutral, Spaces and Punctuation and such --
            StartNBSPCluster,          // NBSP  -- 
            StartConsonantCluster,     // Consonant -- 
            StartConsonantCluster,     // Consonant with Nukta --
            ToMatraState,              // Nukta     (mark) 
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,              // ZWNJ
            StartCluster,              // Unknown class -- 
            NoChange,NoChange,         // padding
 
 
            //FinalState                Input Char Class
            // This state is identical to StartState 
            StartCluster | InvalidBase,  // Halant    (mark)
            StartCluster | InvalidBase,  // Vedic    (mark)
            StartCluster | InvalidBase,  // VowelSign (mark)
            StartCluster | InvalidBase,  // Matra 
            StartRephConsonantCluster,  // Ra --
            StartCluster,                // Neutral, Spaces and Punctuation and such -- 
            StartNBSPCluster,            // NBSP  -- 
            StartConsonantCluster,     // Consonant --
            StartConsonantCluster,     // Consonant with Nukta -- 
            StartCluster | InvalidBase,  // Nukta    (mark)
            StartVowelCluster,         // Vowel    (base)
            StartStandaloneCluster | InvalidBase, // ZWJ   (assume it needs a dotted circle)
            StartCluster,          // ZWNJ 
            StartCluster,                // Unknown class --
            NoChange,NoChange,         // padding 
 
        };
    } 

    internal struct IndicClusterCop
    {
        IndicScriptIndices _scriptIx; 
        ConsonantState    _consonantState;
 
        IndicCharClassifier _charConverter; 
        IndicFormFlags     _matraPositionFlags;
 
        Feature[]   _textFeatures;
        uint[]      _featureTags;
        IndicFontClient _fontClient;
 
        ushort     _firstCharIx;
 
        ushort     _lastHalantOffset; 
        ushort     _lastConsonantOffset;
        ushort     _lastNuktaOffset; 
        ushort     _preMainMatraOffset;
        ushort     _vowelOffset;
        ushort     _clusterSize;
        ushort     _unmappedGlyphsCount; 

        ushort      _movedToBeforeSubCount; 
        ushort      _movedToBeforePostCount; 
        ushort      _movedToAfterSubCount;
        ushort      _movedToAfterPostCount; 
        ushort      _movedToBeforeEndCount;

        ushort     _halfOffset;
        ushort     _mainOffset; 
        ushort     _subOffset;
        ushort     _postOffset; 
        ushort     _lastMainOffset; 
        ushort     _rephOffset;
        ushort     _postbaseRaOffset; 
        ushort     _ZWJOffset;

        bool       _useV2FontRules;
 
        bool       _isVowelPresent;
        bool       _isZWJPresent; 
        bool       _isMatraPresent; 
        bool       _isSplitMatraPresent;
 

        bool       _isNuktaPresent;
        bool       _isHalantActive;
        bool       _isMatraReorderPending; 
        bool       _isPostMatraHalantPresent;
        bool       _isPostMatraNuktaPresent; 
        bool       _isRephReorderPending; 
        bool       _isVattuPresent;
 
        bool       _isReorderingNecessary;
        bool       _gurmukhiRephGoesAfterPostMatra;

        IndicRepositioningClass _lastMatraRepositioningClass; 

        private const CharShapeInfo RepositioningClassMask = 
                            (CharShapeInfo)IndicRepositioningClass.RepositioningClassMask; 

        ///  
        /// Critical - calls critical code
        /// 
        [SecurityCritical]
        public IndicClusterCop( IndicFontClient fontClient ) 
        {
            _firstCharIx = 0; 
            _vowelOffset = _mainOffset = _lastMainOffset = 0; 
            _subOffset = _postOffset = 0;
            _rephOffset = _postbaseRaOffset = 0; 
            _lastConsonantOffset = _lastHalantOffset = _lastNuktaOffset = 0;
             _preMainMatraOffset = 0;
            _ZWJOffset = 0;
            _halfOffset = 0xffff; 

            _movedToBeforeSubCount  = 0; 
            _movedToBeforePostCount = 0; 
            _movedToAfterSubCount  = 0;
            _movedToAfterPostCount = 0; 
            _movedToBeforeEndCount = 0;
            _isReorderingNecessary = false;

            _consonantState = ConsonantState.Start; 
            _isVowelPresent = false;
            _isZWJPresent = false; 
            _isNuktaPresent = false; 
            _isHalantActive = false;
            _isRephReorderPending = false; 
            _isVattuPresent = false;
            _gurmukhiRephGoesAfterPostMatra = false;

            _matraPositionFlags = 0; 
            _isMatraPresent = _isSplitMatraPresent = false;
            _isMatraReorderPending = false; 
            _isPostMatraNuktaPresent = _isPostMatraHalantPresent = false; 

            _lastMatraRepositioningClass = IndicRepositioningClass.AtStart; 

            _clusterSize = 0;
            _unmappedGlyphsCount = 0;
            _textFeatures = new Feature[1]; 
            _textFeatures[0] = new Feature(0,0,0,0);
 
            _fontClient = fontClient; 
            _charConverter = fontClient.IndicCharConverter;
            _featureTags = fontClient.ScriptGsubFeaturesList; 

            // new style fonts expect post-main consonant features
            // to be applied starting at the preceding halant, whereas
            // the old style fonts expect the post-main consonants to 
            // be reversed from h+C to C+h before applying the features
            _useV2FontRules = fontClient.IsIndicV2Font; 
            _scriptIx = _charConverter.ScriptIx; 

 
        }

        public void ResetClusterInfo( ushort firstCharInCluster )
        { 
            if (_consonantState != ConsonantState.Start)
            { 
                _consonantState = ConsonantState.Start; 

                _isVowelPresent = false; 
                _isZWJPresent = false;
                _isNuktaPresent = false;
                _isHalantActive = false;
                _isVattuPresent = false; 
                _gurmukhiRephGoesAfterPostMatra = false;
 
                _mainOffset = _lastMainOffset = 0; 
                _subOffset = _postOffset = 0;
                _rephOffset = 0; 
                _lastConsonantOffset = 0;
                _preMainMatraOffset = 0;

                _halfOffset = 0xffff; 
            }
 
            _firstCharIx = firstCharInCluster; 
            _matraPositionFlags = 0;
            _lastHalantOffset = _lastNuktaOffset = 0; 

            _isMatraPresent = _isSplitMatraPresent = _isPostMatraNuktaPresent = _isPostMatraHalantPresent = false;
            _clusterSize = 0;
            _unmappedGlyphsCount = 0; 
        }
 
        ///  
        ///   IndicClusterCop.ApplyClusterFeatures - finalize the preceding cluster.  This means do
        ///         any reordering and apply features as appropriate 
        /// 
        /// 
        ///   This routine reorders matras, applies all the features that need to be applied,
        ///   and does any final reordering 
        /// 
        /// state flags for current character appropriate to new state 
        ///  
        /// Critical - calls critical code, uses unsafe accessors
        ///  
        [SecurityCritical]
        internal void ApplyClusterFeatures(ref ShapingWorkspace currentRun )
        {
            if (_clusterSize > 1) 
            {
                FinalizeOffsets(  ref currentRun, _clusterSize, true ); // make sure cluster offsets are right 
 
                bool isInitFormPending = false;
                ushort preBaseRephGlyphIx = 0; 
                ushort preBaseRephGlyph = 0;

                // apply features
                for (int i = 0; i < _featureTags.Length; ++i) 
                {
                    _textFeatures[0].Tag = _featureTags[i]; 
                    _textFeatures[0].Parameter = 1; 
                    _textFeatures[0].StartIndex = _firstCharIx;
                    _textFeatures[0].Length = _clusterSize; 

                    switch ( _featureTags[i] )
                    {
                        case (uint)FeatureTags.InitialForms: 
                            // this better be the last Bengali feature
                            Debug.Assert( i + 1 == _featureTags.Length, "init isn't last feature!") ; 
                            _textFeatures[0].Length = 1; 
                            isInitFormPending = true;
                            continue;        // wait till final reordering is done 

                        case (uint)FeatureTags.NuktaForms:
                            if (!_isNuktaPresent)
                            { 
                                continue;   // don't do this one
                            } 
                            break; 

                        case (uint)FeatureTags.Akhands: 
                            // Since I've changed the order of applying reph and akhand features
                            // I don't think I need to worry about applying this feature to the
                            // reph because the reph has already formed (so shouldn't participate
                            // in any lookups for this feature) 
                            if (/*_rephOffset > 0 || */_postbaseRaOffset > 0)
                            { 
                                /*ushort startOffset = 0;     // 
                                if (_rephOffset > 0 &&
                                    (_postbaseRaOffset == 0 || _postbaseRaOffset > _rephOffset)) 
                                {
                                    // don't apply this feature to reph (or the prebase ra, whichever's
                                    // first) Since there may be characters after the reph, apply to the
                                    // reph, then apply to everything after the reph... 
                                    _textFeatures[0].Length = _rephOffset;
                                    _fontClient.SubstituteGlyphs( ref currentRun, 
                                                                 _textFeatures, 
                                                                 1 );
 
                                    // start again past the reph.  If necessary, use LigatureCounts to take
                                    // account of the possibility that we've said the reph is a single
                                    // character due to split matra adjustments
                                    startOffset = 
                                        (ushort)(_rephOffset + (!_isSplitMatraPresent ? (ushort)1 :
                                        currentRun.GlyphInfoList.LigatureCounts[firstGlyphIx + _rephOffset])); 
 
                                    _textFeatures[0].Length = (ushort)(_clusterSize - startOffset);
 
                                }
                                if (_postbaseRaOffset > 0)
                                {
                                    if (_postbaseRaOffset <= startOffset) 
                                    {
                                        continue;   // must be right after reph.  We're done with akhands. 
                                    } 
                                */
                                    // don't apply this feature to Malayalam prebase ra 
                                    _textFeatures[0].Length = (ushort)(_postbaseRaOffset );
                                /*}

                                if (startOffset >= _clusterSize) 
                                {
                                    continue; 
                                } 
                                _textFeatures[0].StartIndex = (ushort)(_firstCharIx + startOffset);
                                 */ 

                            }
                            break;
 
                        case (uint)FeatureTags.RephForm:
                            if (_rephOffset == 0) 
                            { 
                                continue;   // don't do this one
                            } 
                            _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _rephOffset); // only apply to reph
                            if (_isSplitMatraPresent)
                            {
                                ushort rephGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _rephOffset)); 
                                _textFeatures[0].Length =
                                        currentRun.GlyphInfoList.LigatureCounts[rephGlyphIx]; 
 
                            }
                            else 
                            {
                                _textFeatures[0].Length = 2;
                            }
                            break; 

                        case (uint)FeatureTags.PrebaseForms: 
                            if ( _postbaseRaOffset == 0 || !_useV2FontRules ) 
                            {
                                continue;   // don't do this one 
                            }
                            else
                            {
                                ushort prebaseRephIx = (ushort)(_firstCharIx + _postbaseRaOffset); 
                                preBaseRephGlyphIx = currentRun.GetGlyphIx(prebaseRephIx);
                                preBaseRephGlyph = currentRun.GetGlyph(preBaseRephGlyphIx); 
 
                                _textFeatures[0].StartIndex = prebaseRephIx;
                                _textFeatures[0].Length = _isSplitMatraPresent ? 
                                    currentRun.GlyphInfoList.LigatureCounts[preBaseRephGlyphIx] :
                                    (ushort) 2;
                            }
                            break; 

                        case (uint)FeatureTags.BelowBaseForms: 
                            if (_subOffset > 0 || _lastMainOffset > _mainOffset) 
                            {
                              // apply to consonants pre main to last sub 
                              _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _mainOffset + 1);
                              // apply through sub consonants
                            }
                            else if (!_isVattuPresent) 
                            {
                                continue;       // don't apply this one 
                            } 

                            _textFeatures[0].Length = 
                                (ushort)((_postOffset > 0 ? _postOffset : _clusterSize)
                                            - ( _mainOffset + 1));
                            break;
 
                        case (uint)FeatureTags.HalfForms:
                            if (_lastMainOffset <= _halfOffset) 
                            { 
                                continue;       // don't apply this one
                            } 

                             // apply to consonants from first half to last main
                            _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _halfOffset);
                             _textFeatures[0].Length = (ushort)(_lastMainOffset - _halfOffset); 
                            break;
 
                        case (uint)FeatureTags.PostbaseForms: 
                            // pre-Vista kartika font uses PSTF feature to form prebase rephs
                            if ( _postbaseRaOffset != 0 && !_useV2FontRules) 
                            {
                                ushort prebaseRephIx = (ushort)(_firstCharIx + _postbaseRaOffset);
                                preBaseRephGlyphIx = currentRun.GetGlyphIx(prebaseRephIx);
                                preBaseRephGlyph = currentRun.GetGlyph(preBaseRephGlyphIx); 
                                if (_postbaseRaOffset < _postOffset)
                                { 
                                    _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _postbaseRaOffset); 
                                    _textFeatures[0].Length = (ushort)(_clusterSize - _postbaseRaOffset);
                                    break; 
                                }
                            }
                            else if (_postOffset == 0)
                            { 
                                continue;       // don't apply this one
                            } 
 
                             // apply to post consonants
                            _textFeatures[0].StartIndex = (ushort)(_firstCharIx + _postOffset); 
                            _textFeatures[0].Length = (ushort)(_clusterSize - _postOffset);


                            break; 

                        case (uint)FeatureTags.VattuVariants: 
                            if (!(_isVattuPresent || _subOffset > 0)) 
                            {
                                continue;       // don't apply this one 
                            }
                            break;

                         default: 
                            break;
                        } 
 
                        //  Apply the Indic Text Features from currentRun
                        _fontClient.SubstituteGlyphs( ref currentRun, 
                                                     _textFeatures,
                                                     1 );
                        if (preBaseRephGlyphIx != 0)
                        { 
                            // check if the first prebase ra glyph is changed.  If not, prebase ra
                            // didn't form 
                            if (currentRun.GetGlyph(preBaseRephGlyphIx) == preBaseRephGlyph) 
                            {
                                _postbaseRaOffset = 0; 
                            }
                            preBaseRephGlyphIx = 0;
                        }
                } 

                // do post-feature reordering 
                ReorderClusterFinal( ref currentRun ); 

                if (isInitFormPending) 
                {
                    //  Apply the Bengali init feature to the first character
                    _fontClient.SubstituteGlyphs( ref currentRun, _textFeatures, 1 );
                } 
            }
            else 
            { 
                // the previous cluster is a single character cluster.  The only thing we need
                // to do is check if its a dotted circle with a prebase matra 
                ushort previousCharIx =
                    currentRun.IsFinished ? currentRun.CurrentCharIx : currentRun.PreviousCharIx;
                CharShapeInfo previousShape = currentRun.GetShapeInfo(previousCharIx);
                if ((previousShape & CharShapeInfo.RequiresInsertedBase) == 
                                                    CharShapeInfo.RequiresInsertedBase)
                { 
                    // the cluster is a "dotted circle" cluster.  So, check for prebase 
                    // matra.  First test is for a split matra.  If its a split matra
                    // use the repositioning info for the first component matra 
                    if ((IndicCharClass)(previousShape & IndicShape.IndicCharClassMask)
                                                                        == IndicCharClass.Matra)
                    {
                        char previousChar = currentRun.GetChar(previousCharIx); 
                        CharShapeInfo matraShapeInfo = _charConverter.ToShapeInfo(previousChar);
                        bool isSplitMatra = 
                            ((matraShapeInfo & IndicShape.ShapeIncludesPositioningInfoFlag) == 0); 
                        if (isSplitMatra)
                        { 
                            char [] matraComponents =
                                _charConverter.ToDecompositionList(previousChar);
                            if (matraComponents != null)
                            { 
                               matraShapeInfo = (CharShapeInfo)_charConverter.ToRepositioningClass(matraComponents[0]);
                            } 
                        } 
                        else
                        { 
                               matraShapeInfo = (CharShapeInfo)GetRepositioningClass(matraShapeInfo);
                        }

                        // Now check if matra's a before main matra... 
                        if ((ushort)matraShapeInfo <=
                             (ushort)IndicRepositioningClass.BeforeMain) 
                        { 
                            // the matra is "before main" so
                            // swap the dotted circle glyph and its first matra 
                            ushort dottedCircleGlyphIx = currentRun.GetGlyphIx(previousCharIx);
                            currentRun.SetGlyph(dottedCircleGlyphIx,
                                currentRun.GetGlyph((ushort)(dottedCircleGlyphIx + 1)));
                            currentRun.SetGlyph((ushort)(dottedCircleGlyphIx + 1), 
                                                        _fontClient.DottedCircleGlyph);
                        } 
                    } 

                } 
            }

        }
 
        /// 
        /// Critical - calls critical code 
        ///  
        [SecurityCritical]
        private bool ReorderClusterFinal(ref ShapingWorkspace currentRun) 
        {
                // now that initial shaping has been done, make adjustments if
                // necessary to properly reposition matras/reph when we have
                // a non-conjunct forming sequence of pre-sub consonants 

            if (_isReorderingNecessary) 
            { 
                ushort toPosition = 0;
                bool foundReorderingAnchor = FindLateRepositionAnchor(ref currentRun); 

                // do the reordering necessary...
                // (first do matras, vowel signs, then reph)
                if (_isMatraReorderPending) 
                {
                   if (foundReorderingAnchor) 
                   { 
                        toPosition = _lastMainOffset;
                        _mainOffset -= 1; 

                       ushort glyphsCount = 1;
                       ushort matraGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _preMainMatraOffset));
                       while (matraGlyphIx + glyphsCount < currentRun.GlyphsCount && 
                              currentRun.GlyphInfoList.FirstChars[matraGlyphIx + glyphsCount] ==
                              currentRun.GlyphInfoList.FirstChars[matraGlyphIx]) 
                       { 
                            glyphsCount += 1;
                       } 
                       RepositionGlyphs( ref currentRun, _preMainMatraOffset, toPosition,
                                          1, glyphsCount);
                   }
 
                   if (_isRephReorderPending && _postbaseRaOffset == 0)
                   { 
                        foundReorderingAnchor = FindLateRepositionAnchor(ref currentRun); 
                   }
 
                    _preMainMatraOffset = 0;
                    _isMatraReorderPending = false;
                }
 

                // if there's a pre-base reph to move, nows the time... 
                if (_postbaseRaOffset > 0) 
                {
                    // move the post base ra to the appropriate main consonant 

                    toPosition = _mainOffset;
                    if (toPosition < _postbaseRaOffset)
                    { 
                        ushort raGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _postbaseRaOffset));
                        ushort charCount = currentRun.GlyphInfoList.LigatureCounts[raGlyphIx]; 
 
                        if (_postbaseRaOffset > _lastMainOffset)
                        { 
                            toPosition = _lastMainOffset;
                            _lastMainOffset += charCount;
                        }
                        if (_postbaseRaOffset > _rephOffset && toPosition <= _rephOffset) 
                        {
                            _rephOffset += charCount; 
                        } 

                        RepositionGlyphs( ref currentRun, _postbaseRaOffset, toPosition, charCount, 1 ); 
                    }

                   if (_isRephReorderPending)
                   { 
                        foundReorderingAnchor = FindLateRepositionAnchor(ref currentRun);
                   } 
                   _postbaseRaOffset = 0; 
                }
 
                // And, finally, if there's a reph to revisit do it now...
                if (_isRephReorderPending)
                {
                   _isRephReorderPending = false; 

                   if ( foundReorderingAnchor && _rephOffset > _lastMainOffset ) 
                   { 
                        ushort rephGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _rephOffset));
                        ushort charCount = currentRun.GlyphInfoList.LigatureCounts[rephGlyphIx]; 
                        RepositionGlyphs( ref currentRun, _rephOffset, _lastMainOffset, charCount, 1 );
                   }
                }
 

            } 
 

            // done reordering, reset all these counters... 
            _movedToBeforeSubCount  = 0;
            _movedToBeforePostCount = 0;
            _movedToAfterSubCount  = 0;
            _movedToAfterPostCount = 0; 
            _movedToBeforeEndCount = 0;
 
            _isMatraPresent = _isPostMatraNuktaPresent = _isPostMatraHalantPresent = false; 

            _isReorderingNecessary = false; 

            return true;
        }
 
        private ushort GetRepositionOffsetAndUpdateCluster (IndicRepositioningClass repositionClass,
                                                                        bool isReph, 
                                                                        ushort charsMovingCount) 
        {
            ushort offsetFromEnd = 0; 
            ushort toPosition = 0;
            bool   useOffsetFromEnd = false;

            switch (repositionClass) 
            {
                case IndicRepositioningClass.AtStart: 
                    toPosition = _halfOffset <= _mainOffset ? _halfOffset : _mainOffset; 
                    goto MovingToBeforeMain;
 
                case IndicRepositioningClass.BeforeMain:
                    toPosition = _mainOffset;

MovingToBeforeMain: 
                    if (_lastMainOffset > toPosition)
                    { 
                        // note this position (we're still not sure if the main 
                        // consonants will be forming a conjunct form).  If
                        // last main > main consonant ix, then we'll need to 
                        // check if matra needs reordering after features are
                        // applied.  (if Malayam, check for reordering if
                        // last main > half offset)
                        if (toPosition == _mainOffset || _scriptIx == IndicScriptIndices.Malayalam) 
                        {
                            _preMainMatraOffset = toPosition; 
                            _isReorderingNecessary = true; 
                            _isMatraReorderPending = true;
                        } 
                    }

                    // adjust the premain and "main" consonants' offsets
                    // so that they are now after this newly inserted matra/sign/prebase ra 
                    if (_halfOffset == toPosition)
                    { 
                        _halfOffset += charsMovingCount; 
                    }
 
                    _mainOffset += charsMovingCount;
                    _lastMainOffset += charsMovingCount;

                    break; 

                case IndicRepositioningClass.AfterMain: 
                    if (_subOffset > 0) 
                    {
                        toPosition = (ushort)(_subOffset - _movedToBeforeSubCount); 
                    }
                    else if (_postOffset > 0)
                    {
                        toPosition = (ushort)(_postOffset - _movedToBeforePostCount); 
                    }
                    else 
                    { 
                        useOffsetFromEnd = true;
                        offsetFromEnd = (ushort) 
                            (   _movedToBeforeSubCount +
                                _movedToAfterSubCount  +
                                _movedToBeforePostCount +
                                _movedToAfterPostCount + 
                                _movedToBeforeEndCount );
                    } 
                    break; 

                case IndicRepositioningClass.BeforeSub: 

                    if (_subOffset > 0)
                    {
                        toPosition = _subOffset; 
                    }
                    else if (_postOffset > 0) 
                    { 
                        toPosition = (ushort)(_postOffset - _movedToBeforePostCount);
                    } 
                    else
                    {
                        useOffsetFromEnd = true;
                        offsetFromEnd = (ushort) 
                            (   _movedToAfterSubCount  +
                                _movedToBeforePostCount + 
                                _movedToAfterPostCount + 
                                _movedToBeforeEndCount );
 
                    }

                    _movedToBeforeSubCount += charsMovingCount;
                    break; 

 
                case IndicRepositioningClass.AfterSub: 
                    if ( _postOffset > 0)
                    { 
                         toPosition = (ushort)(_postOffset - _movedToBeforePostCount);  // put before post offset
                    }
                    else
                    { 
                        useOffsetFromEnd = true;
                        offsetFromEnd = (ushort) 
                                        ( _movedToBeforePostCount + 
                                          _movedToAfterPostCount +
                                          _movedToBeforeEndCount ); 
                    }

                    _movedToAfterSubCount += charsMovingCount;
                    break; 

                case IndicRepositioningClass.BeforePost: 
                    if ( _postOffset > 0) 
                    {
                         toPosition = _postOffset;  // put before post offset 
                    }
                    else
                    {
                        useOffsetFromEnd = true; 

                        if (isReph) 
                        { 
                            // special case for Gurmukhi - when there's no post consonant but
                            // there is an after-post matra, then move the reph after the 
                            // matra (unless its one of the two matras,  0xa3e or 0xa40)
                            if (_gurmukhiRephGoesAfterPostMatra)
                            {
                                _gurmukhiRephGoesAfterPostMatra = false; 

                                offsetFromEnd = (ushort)( _movedToBeforeEndCount ); 
                                _movedToAfterPostCount += charsMovingCount; 
                                break;
                            } 
                        }

                        offsetFromEnd = (ushort)
                                        ( _movedToAfterPostCount + 
                                          _movedToBeforeEndCount );
                   } 
 

                    _movedToBeforePostCount += charsMovingCount; 
                    break;

                case IndicRepositioningClass.AfterPost:
                    useOffsetFromEnd = true; 
                    offsetFromEnd = (ushort)( _movedToBeforeEndCount );
 
                    _movedToAfterPostCount += charsMovingCount; 
                    break;
 
                case IndicRepositioningClass.BeforeEnd:
                    useOffsetFromEnd = true;
                    offsetFromEnd = 0;
 
                    _movedToBeforeEndCount += charsMovingCount;
                    break; 
 
                default:
                    throw new NotSupportedException(); 

            }

            // default return 
            if (useOffsetFromEnd)
            { 
                toPosition = (ushort)( _clusterSize + _unmappedGlyphsCount - offsetFromEnd ); 
            }
 
            if (charsMovingCount > 0)
            {
                if (isReph)
                { 
                    // adjust all the cluster offsets for a reph being positioned before feature
                    // application 
                    _halfOffset -= charsMovingCount; 
                    _mainOffset -= charsMovingCount;
                    _lastMainOffset -= charsMovingCount; 
                    if (_postbaseRaOffset > 0 && _postbaseRaOffset <= toPosition)
                    {
                        if (_postbaseRaOffset == toPosition)
                        { 
                            toPosition += charsMovingCount;    // if positioning reph in front of prebase ra, move past it
                        } 
                        else if ((ushort)(_postbaseRaOffset + 1) == toPosition) 
                        {
                            toPosition += 1;    // if positioning reph in front of prebase ra, move past it 
                        }
                        _postbaseRaOffset -= charsMovingCount;
                    }
 
                    if (_subOffset > 0 && _subOffset < toPosition) _subOffset -= charsMovingCount;
                    if (_postOffset > 0 && _postOffset < toPosition) _postOffset -= charsMovingCount; 
                    if (_preMainMatraOffset > 0 && _preMainMatraOffset < toPosition) _preMainMatraOffset -= charsMovingCount; 

                    if (_isPostMatraNuktaPresent && _lastNuktaOffset < toPosition) _lastNuktaOffset -= charsMovingCount; 
                    _rephOffset = (ushort)(toPosition - charsMovingCount);
                }
                else
                { 
                    // make adjustments for any matra, sign
                    if (_subOffset > 0 && _subOffset >= toPosition) _subOffset += charsMovingCount; 
                    if (_postOffset > 0 && _postOffset >= toPosition) _postOffset = (ushort)((int)_postOffset + charsMovingCount); 
                    if (_postbaseRaOffset > 0 && _postbaseRaOffset >= toPosition) _postbaseRaOffset = (ushort)((int)_postbaseRaOffset + charsMovingCount);
                    if (_rephOffset > 0 && _rephOffset >= toPosition) _rephOffset = (ushort)((int)_rephOffset + charsMovingCount); 
                    if (_isPostMatraNuktaPresent && _lastNuktaOffset >= toPosition) _rephOffset = (ushort)((int)_rephOffset + charsMovingCount);
                }
            }
 
            return toPosition;
        } 
 

        ///  
        /// Critical - calls critical code
        /// 
        [SecurityCritical]
        private void RepositionGlyphs( ref ShapingWorkspace currentRun, 
                                      ushort fromPosition, ushort toPosition,
                                      ushort charsToMove, ushort glyphsToMove) 
        { 
            // do the glyph move
            GlyphInfoList glyphs = currentRun.GlyphInfoList; 
            UshortList charMap = currentRun.CharMap;
            ushort nextGlyphIx, previousGlyphIx;
            ushort nextCharIx, previousCharIx;
 
            // set up the "glyphs to move" variables...
            fromPosition += _firstCharIx; 
            toPosition += _firstCharIx; 
            ushort firstGlyphToMove = currentRun.GetGlyphIx(fromPosition);
 
            if (toPosition > fromPosition)
            {
                   // moving glyphs backwards (toward the end of the cluster)
                   // This must be a before main matra that needs to be moved 
                   // back onto the last component in a non-conjunct forming
                   // multi-consonant sequence. 
                ushort charMoveDistance = (ushort)(toPosition - fromPosition); 
                ushort glyphMoveToPosition = (ushort)
                   (( toPosition == currentRun.CharsCount ? 
                                    currentRun.GlyphsCount :
                                    currentRun.GetGlyphIx( toPosition ) ) - 1 );
                ushort glyphMoveDistance = (ushort)(glyphMoveToPosition - firstGlyphToMove);
 
                if (glyphMoveDistance == 0)
                { 
//                    Debug.Assert (glyphMoveDistance > 0,"glyph move distance is zero!"); 
                    return;
                } 

                   // go through all the glyphs, removing one at a time from the front
                   // of the block and reinserting it at the back of the block
                 for (int i = 0; i < glyphsToMove; ++i) 
                 {
                   ushort movingGlyph      = glyphs.Glyphs[firstGlyphToMove]; 
                   ushort movingLigCount   = glyphs.LigatureCounts[firstGlyphToMove]; 
                   ushort movingFlags      = glyphs.GlyphFlags[firstGlyphToMove];
 
                   for ( nextGlyphIx = firstGlyphToMove;
                         nextGlyphIx < glyphMoveToPosition;
                         ++nextGlyphIx)
                   { 
                       previousGlyphIx = (ushort)(nextGlyphIx + 1);
 
                       glyphs.Glyphs[nextGlyphIx] = glyphs.Glyphs[previousGlyphIx]; 
                       glyphs.LigatureCounts[nextGlyphIx] = glyphs.LigatureCounts[previousGlyphIx];
                       glyphs.GlyphFlags[nextGlyphIx] = glyphs.GlyphFlags[previousGlyphIx]; 
                   }

                   glyphs.Glyphs[glyphMoveToPosition] = movingGlyph;
                   glyphs.LigatureCounts[glyphMoveToPosition] =  movingLigCount; 
                   glyphs.GlyphFlags[glyphMoveToPosition] = movingFlags;
                 } 
 
                // go through the first chars now and fix 'em up.  The intent is to
                // have the effect of removing "charsToMove" characters from the front 
                // of the block of characters and add them to the back of the character
                // block.  Of course, we aren't really moving the characters, but just
                // setting the glyphs' "first char" pointer and the charmap values as
                // if we had moved the characters.  Similarly, we have removed "glyphsToMove" 
                // glyphs from the front of the glyphs and added it to the back.
                // We didn't do this in the loop above because we might do the loop several 
                // times (if glyphsToMove is greater than one). 
                nextCharIx = (ushort)(toPosition - charsToMove);
                nextGlyphIx = firstGlyphToMove; 
                for (int i = 0; i < glyphMoveDistance; ++i, ++nextGlyphIx)
                {
                    previousCharIx = glyphs.FirstChars[ nextGlyphIx + glyphsToMove ];
                    if (previousCharIx >= fromPosition) 
                    {
                        glyphs.FirstChars[ nextGlyphIx ] = 
                                   (ushort)(previousCharIx - charsToMove); 
                    }
                    else 
                    {
                         glyphs.FirstChars[ nextGlyphIx ] = previousCharIx;
                    }
                } 
                // finish up the inserted (moved) glyphs. There's always one character
                // per glyph 
                while (nextGlyphIx <= glyphMoveToPosition) 
                {
                    glyphs.FirstChars[nextGlyphIx++] = nextCharIx++; 
                }

                // do the same thing for the charmap
                nextCharIx = fromPosition; 
                nextGlyphIx = (ushort)(glyphMoveToPosition - glyphsToMove);
                for (int i = 0; i < charMoveDistance; ++i, ++nextCharIx) 
                { 
                    previousGlyphIx = charMap[nextCharIx + charsToMove];
                    if (previousGlyphIx >= firstGlyphToMove) 
                    {
                        charMap[nextCharIx] = (ushort)(previousGlyphIx - glyphsToMove);
                    }
                    else 
                    {
                        charMap[nextCharIx] = previousGlyphIx; 
                    } 
                }
                while (nextCharIx < toPosition) 
                {
                   charMap [ nextCharIx++ ] = ++nextGlyphIx;
                }
            } 
            else if (toPosition < fromPosition)
            { 
                 // moving glyphs forward (toward the beginning of the cluster) 
                 // These are ra's and prebase ra's...
                 ushort glyphMoveToPosition = currentRun.GetGlyphIx( toPosition ); 
                 ushort charMoveDistance = (ushort)(fromPosition - toPosition);
                 ushort glyphMoveDistance = (ushort)(firstGlyphToMove - glyphMoveToPosition);
                 if (glyphMoveDistance == 0)
                 { 
//                    Debug.Assert (glyphMoveDistance > 0,"glyph move distance is zero!");
                    return; 
                 } 

 
                 for (int i = 0; i < glyphsToMove; ++i)
                 {
                   nextGlyphIx = (ushort)(firstGlyphToMove + i);
                   previousGlyphIx = (ushort)(nextGlyphIx - 1); 
                   ushort insertedGlyphPosition = (ushort)(glyphMoveToPosition + i);
 
                   ushort movingGlyph      = glyphs.Glyphs[nextGlyphIx]; 
                   ushort movingLigCount   = glyphs.LigatureCounts[nextGlyphIx];
                   ushort movingFlags      = glyphs.GlyphFlags[nextGlyphIx]; 

                   while (nextGlyphIx > insertedGlyphPosition)
                   {
                       glyphs.Glyphs[nextGlyphIx] = glyphs.Glyphs[previousGlyphIx]; 
                       glyphs.LigatureCounts[nextGlyphIx] =  glyphs.LigatureCounts[previousGlyphIx];
                       glyphs.GlyphFlags[nextGlyphIx--] = glyphs.GlyphFlags[previousGlyphIx--]; 
                   } 

                   glyphs.Glyphs[nextGlyphIx] = movingGlyph; 
                   glyphs.LigatureCounts[nextGlyphIx] =  movingLigCount;
                   glyphs.GlyphFlags[nextGlyphIx] = movingFlags;

                 } 

                // go through the first chars now and fix 'em up.  The intent is to 
                // have the effect of adding "charsToMove" characters to the front 
                // of the block of characters and removing them from the back of the character
                // block.  Of course, we aren't really moving the characters, but just 
                // setting the glyphs' "first char" pointer and the charmap values as
                // if we had moved the characters.  Similarly, we have removed "glyphsToMove"
                // glyphs from the back of the glyphs and added it to the front.
                // We didn't do this in the loop above because we might do the loop several 
                // times (if glyphsToMove is greater than one).
                nextGlyphIx = (ushort)(firstGlyphToMove + glyphsToMove - 1); 
                for (int i = 0; i < glyphMoveDistance; ++i, --nextGlyphIx) 
                {
                    previousCharIx = glyphs.FirstChars[ nextGlyphIx - glyphsToMove ]; 
                    if (previousCharIx >= toPosition )
                    {
                        glyphs.FirstChars[ nextGlyphIx ] =
                                (ushort)(previousCharIx + charsToMove); 
                    }
                    else 
                    { 
                        glyphs.FirstChars[ nextGlyphIx ] = previousCharIx;
                    } 
                }

                // finish up the inserted (moved) glyphs.  We either have one glyph for
                // multiple characters (like for reph's) or one glyph per character (as 
                // for normal matras), or zero characters for one glyph (for split matra
                // components) 
                nextCharIx = (ushort)(toPosition + charsToMove - 1); 
                while (nextGlyphIx > glyphMoveToPosition)
                { 
                    glyphs.FirstChars[nextGlyphIx--] = nextCharIx--;
                }
                glyphs.FirstChars[glyphMoveToPosition] = nextCharIx;
 
                // do the same thing for the charmap
                nextCharIx = (ushort)(fromPosition + charsToMove - 1); 
                nextGlyphIx = (ushort)(glyphMoveToPosition + glyphsToMove - 1); 
                for (int i = 0; i < charMoveDistance; ++i, --nextCharIx)
                { 
                    previousGlyphIx = charMap[nextCharIx - charsToMove];
                    if (previousGlyphIx >= glyphMoveToPosition)
                    {
                        charMap[nextCharIx] = (ushort)(previousGlyphIx + glyphsToMove); 
                    }
                    else 
                    { 
                        charMap[nextCharIx] = previousGlyphIx;
                    } 
                }
                while (nextCharIx > toPosition)
                {
                  charMap [ nextCharIx-- ] = nextGlyphIx; 
                  if (glyphsToMove > 1)  --nextGlyphIx;
                } 
                charMap [ toPosition ] = glyphMoveToPosition; 

            } 

        }

 
        /// 
        ///     IndicClusterCop.RepositionCharacters - 
        ///          . 
        /// 
        /// shaping currentRun 
        /// actual character ix (relative to start of cluster) to be moved
        /// "virtual" character ix for glyph to move to
        /// number of glyphs that are to be moved
        /// If there're no split matra components in the cluster then the 
        ///          "virtual" character ix corresponds exactly to the the
        ///          real character ix in the charmap.  If there are split matra 
        ///          components the "virtual" ix is the actual glyph ix (relative to 
        ///          first glyph in the cluster), but not correspond to the correct
        ///          character ix. 
        /// 
        /// Critical - calls critical code
        /// 
        [SecurityCritical] 
        private void RepositionCharacters (ref ShapingWorkspace currentRun,
                                      ushort fromPosition, 
                                      ushort toPosition, 
                                      ushort moveCount )
        { 
            if (fromPosition != toPosition)
            {
                ushort firstGlyphIx = currentRun.GetGlyphIx(_firstCharIx);
                ushort glyphsInCluster = (ushort)(_clusterSize + _unmappedGlyphsCount); 
                fromPosition = (ushort)(firstGlyphIx + fromPosition);
                toPosition = (ushort)(firstGlyphIx + toPosition); 
 
                if ( toPosition > fromPosition )    // if moving the reph (or halant for old style fonts)
                { 
                    --toPosition;                   // move to in front of to position
                    Debug.Assert(toPosition <
                                (ushort)(firstGlyphIx + _clusterSize + _unmappedGlyphsCount),
                                "Invalid reph/halant reposition"); 

                    ushort moveDistance = (ushort)( toPosition - fromPosition ); 
                    if (moveDistance > 0) 
                    {
                        if (_isZWJPresent) 
                        {
                            ushort zwjGlyphIx = (ushort)(firstGlyphIx + _ZWJOffset);
                            if(zwjGlyphIx > fromPosition && zwjGlyphIx <= toPosition)
                            { 
                               currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx] = ShapingWorkspace.GlyphFlagsNone;
                               if (_ZWJOffset >= moveCount) 
                               { 
                                   currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx - moveCount] =
                                                                ShapingWorkspace.GlyphFlagsZeroWidth; 
                                   _ZWJOffset -= moveCount;
                               }
                            }
                        } 

                        for (ushort i = 0; i < moveCount; ++i) 
                        { 
                            ushort movingGlyph = currentRun.GetGlyph(fromPosition);
 
                            currentRun.MoveGlyphs( fromPosition,
                                                   (ushort)(fromPosition + 1),
                                                   moveDistance );
 
                            // now update the repositioned glyph
                            currentRun.SetGlyph(toPosition, movingGlyph); 
                        } 
                    }
                } 
                else if (fromPosition > toPosition) // else if moving matra/sign
                {
                    Debug.Assert((ushort)(fromPosition + moveCount) <= currentRun.GlyphsCount &&
                                 (ushort)(fromPosition + moveCount) <= 
                                 (ushort)(firstGlyphIx + _clusterSize + _unmappedGlyphsCount + 1),
                                 "Invalid matra reposition"); 
                    ushort moveDistance = (ushort)( fromPosition - toPosition ); 

                    if (moveDistance > 0) 
                    {
                        if (_isZWJPresent)
                        {
                            ushort zwjGlyphIx = (ushort)(firstGlyphIx + _ZWJOffset); 
                            if(zwjGlyphIx >= toPosition && zwjGlyphIx < fromPosition)
                            { 
                               currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx] = ShapingWorkspace.GlyphFlagsNone; 
                               if (_ZWJOffset + moveCount < currentRun.GlyphsCount)
                               { 
                                   currentRun.GlyphInfoList.GlyphFlags[zwjGlyphIx + moveCount] =
                                                                ShapingWorkspace.GlyphFlagsZeroWidth;
                                   _ZWJOffset += moveCount;
                               } 
                            }
                        } 
 
                        for (ushort i = 0; i < moveCount; ++i)
                        { 

                            ushort matraGlyph = currentRun.GetGlyph(fromPosition);
                            currentRun.MoveGlyphs( (ushort)(toPosition + 1),
                                                   toPosition, 
                                                   moveDistance );
 
                            // now update the repositioned glyph 
                            currentRun.SetGlyph( toPosition, matraGlyph );
 
                            ++fromPosition;
                            ++toPosition;
                        }
                    } 

                } 
            } 
        }
 
        /// 
        /// Critical - calls critical code
        /// 
        [SecurityCritical] 
        private void RepositionSplitMatra(ref ShapingWorkspace currentRun, ushort matraOffset)
        { 
            // this is a split matra.  Get its components and move them 
            // to the appropriate positions
 
            // decompose the matra and move its glyphs to the right spots
            char [] matraComponents =
                _charConverter.ToDecompositionList(currentRun.GetChar((ushort)(_firstCharIx + matraOffset)));
            if (matraComponents != null) 
            {
                ushort componentIx = 0; 
                // save the positioning info from this new char shape... 
                IndicRepositioningClass nextComponentRepositioningClass =
                        _charConverter.ToRepositioningClass(matraComponents[componentIx]); 

                while (componentIx <  matraComponents.Length)
                {
                    // go through the rest of the components till we find the next one that 
                    // repositions to a different place...
                    IndicRepositioningClass componentRepositioningClass = nextComponentRepositioningClass; 
                    ushort componentsToMove = 1; 
                    ushort toPosition =  GetRepositionOffsetAndUpdateCluster(componentRepositioningClass,
                                                                    false,  // not reph 
                                                                    0);

                    while (++componentIx < matraComponents.Length)
                    { 
                        nextComponentRepositioningClass =
                                _charConverter.ToRepositioningClass(matraComponents[componentIx]); 
                        _unmappedGlyphsCount += 1;         // keep track of number of added component glyphs 
                        if (nextComponentRepositioningClass == componentRepositioningClass)
                        { 
                            ++componentsToMove;
                            continue;
                        }
 
                        break;
                    } 
 
                    RepositionCharacters(ref currentRun, matraOffset, toPosition,componentsToMove);
 
                    // re-call with char count to update the cluster.
                    GetRepositionOffsetAndUpdateCluster(componentRepositioningClass,
                                                        false,  // not reph
                                                        componentsToMove); 
                    matraOffset += componentsToMove;    // this will be our next starting "virtual" character offset
                } 
 
                _lastMatraRepositioningClass = nextComponentRepositioningClass;
                _isSplitMatraPresent = true; 

            }
            Debug.Assert(matraComponents != null, "ERROR - no split matra components array.");
        } 

        ///  
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        public bool AddCharToCluster (ref ShapingWorkspace currentRun)
        {
            CharShapeInfo charShapeFlags = currentRun.CurrentShape;
            bool isClusterOk = true; 

            // before processing the current character, check if its time to process the 
            // preceding cluster... 
            if ((charShapeFlags & CharShapeInfo.IsStartOfCluster) != 0 && _clusterSize > 0)
            { 
                ApplyClusterFeatures( ref currentRun );
                ResetClusterInfo(currentRun.CurrentCharIx);
            }
 
            if ((charShapeFlags & CharShapeInfo.RequiresInsertedBase) !=
                                                        CharShapeInfo.RequiresInsertedBase) 
            { 
                ushort clusterCharOffset = _clusterSize;
                IndicCharClass charClass = 
                    (IndicCharClass)(charShapeFlags & IndicShape.IndicCharClassMask);

                switch (charClass)
                { 
                    case IndicCharClass.Vowel:
                        if (_isVowelPresent) 
                        { 
                            // can't have two vowels per cluster
                            isClusterOk = false; 
                        }
                        else
                        {
                            // got a vowel this cluster 
                            _isVowelPresent = true;
                            _vowelOffset = clusterCharOffset; 
                            isClusterOk = AppendConsonant(ref currentRun, clusterCharOffset ); 
                        };
                        break; 
                    case IndicCharClass.NuktaConsonant:
                        isClusterOk = AppendConsonant(ref currentRun, clusterCharOffset );
                        if (isClusterOk)
                        { 
                            _isNuktaPresent = true;
                            _lastNuktaOffset = clusterCharOffset; 
                        } 
                        break;
                    case IndicCharClass.Ra: 
                    case IndicCharClass.Consonant:
                        isClusterOk = AppendConsonant(ref currentRun, clusterCharOffset );
                        break;
                    case IndicCharClass.ZWJ: 
                        isClusterOk = AppendZWJ(ref currentRun, clusterCharOffset  );
                        break; 
                    case IndicCharClass.ZWNJ: 
                        isClusterOk = AppendZWNJ(ref currentRun, clusterCharOffset  );
                        break; 
                    case IndicCharClass.Halant:
                        isClusterOk = AppendHalant( );
                        if (isClusterOk)
                        { 
                            _lastHalantOffset = clusterCharOffset;
                        } 
                        break; 
                    case IndicCharClass.Nukta:
                        isClusterOk = AppendNukta( ref currentRun, clusterCharOffset ); 
                        if (isClusterOk)
                        {
                            _lastNuktaOffset = clusterCharOffset; // keep track of latest nukta
                        } 
                        break;
 
                    case IndicCharClass.Vedic: 
                    case IndicCharClass.VowelSign:
                        isClusterOk = AppendVowelSign( ref currentRun, clusterCharOffset ); 
                        break;

                    case IndicCharClass.Matra:
                        isClusterOk = AppendMatra( ref currentRun, clusterCharOffset ); 
                        break;
                    case IndicCharClass.NBSP: 
                        break; 
                    default:
                        break; 
                }

                if (!isClusterOk)
                { 
                    // process the cluster up till now
                    Debug.Assert(_clusterSize > 0,"Zero length cluster is illegal"); 
                    ApplyClusterFeatures( ref currentRun ); 
                    ResetClusterInfo ( currentRun.CurrentCharIx );
                } 
            }

            ++_clusterSize;     // this character is part of the cluster
            return isClusterOk; 

        } 
 
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical]
        private bool AppendConsonant ( ref ShapingWorkspace currentRun, ushort consonantCharOffset )
        { 

            _isHalantActive = false; 
 
//            if ( _consonantState == ConsonantState.EndOfClusterReached )
//            { 
//                return false;
//            }

            IndicFormFlags consonantFormFlags = _charConverter.ToFormInfo(currentRun.CurrentChar); 

            // Check if we've already discovered a prebase ra (a rare bird!), and 
            // if so... 
            if (!_useV2FontRules)
            { 
                if (_postbaseRaOffset > 0 &&
                ((consonantFormFlags & IndicFormFlags.PostForm) == 0 ||
                 (consonantFormFlags & IndicFormFlags.PostbaseReph) != 0))
                { 
                    // The old-style Kartika font expects that post-base reordering ra to
                    // have no non-post-base forms after it.  Also, a second post-base ra will 
                    // supercede the first (which means the first is not a post-base form). 
                    // If either of these are true, then set our state to consAfterMain and
                    // clear the prebase ra offset. 
                    // <--
                    _lastMainOffset = _lastConsonantOffset;
                    _consonantState = ConsonantState.AfterMain;
                    _postbaseRaOffset = 0; 
                }
 
            } 

            switch (_consonantState) 
            {
                case ConsonantState.AfterReph:
                    _isRephReorderPending = true;
                    goto FirstMainConsonant; 

                case ConsonantState.AfterZWJ: 
                    goto FirstMainConsonant; 

                case ConsonantState.Start: 
                    if ((consonantFormFlags & IndicFormFlags.RephForm) != 0)
                    {
                        _consonantState = ConsonantState.AfterReph;
                        return true; 
                    }
 
FirstMainConsonant: 
                    // normal handling of the first main consonant (ie, not
                    // a reph).  Also come through here for ZWJ (which is 
                    // why we check whether _halfOffset has already been
                    // set)
                    if ((consonantFormFlags & IndicFormFlags.HalfForm) != 0  &&
                         _halfOffset > consonantCharOffset) 
                    {
                        _halfOffset = consonantCharOffset; 
                    } 

                    _consonantState = (consonantFormFlags & IndicFormFlags.DravidianBase) != 0 ? 
                        ConsonantState.AfterDravidianBase : ConsonantState.AfterMain;

                    _mainOffset = consonantCharOffset;
                    break; 
                case ConsonantState.AfterDravidianBase:
                    // for dravidian base chars (malayalam script only) on old behavior 
                    // fonts, don't shape second consonant 
                    consonantFormFlags = 0; // consonant may not be sub/post after dravidian base
                    goto AfterMain; 

                case ConsonantState.AfterMain:
AfterMain:
                    if ((consonantFormFlags & 
                        (IndicFormFlags.PostForm | IndicFormFlags.SubForm | IndicFormFlags.VattuForm)) != 0)
                    { 
                        // if this is Kannada text and there's a zero width joiner since the 
                        // halant, swap the two...
                        if ( _isZWJPresent && _scriptIx == IndicScriptIndices.Kannada ) 
                        {
                            if ( _ZWJOffset == (ushort)(_lastHalantOffset + 1) )
                            {
                                // For compatibility with legacy useage in Kannada, 
                                // Ra+h+ZWJ must behave like Ra+ZWJ+h.  So always
                                // order these as Ra+ZWJ+h (and, of course, the ZWJ+h 
                                // belong with the following sub.. 
                                ushort firstGlyphIx = currentRun.GetGlyphIx(_firstCharIx);
                                ushort halantGlyphIx = (ushort)(firstGlyphIx + _lastHalantOffset); 
                                ushort zwGlyphIx =  (ushort)(firstGlyphIx + _ZWJOffset);
                                ushort halantFlags = currentRun.GlyphInfoList.GlyphFlags[halantGlyphIx];

                                currentRun.GlyphInfoList.Glyphs[halantGlyphIx] = 
                                                    currentRun.GlyphInfoList.Glyphs[zwGlyphIx];
                                currentRun.GlyphInfoList.GlyphFlags[halantGlyphIx] = 
                                                    currentRun.GlyphInfoList.GlyphFlags[zwGlyphIx]; 

                                currentRun.GlyphInfoList.Glyphs[zwGlyphIx] = 
                                                                    _fontClient.HalantGlyph;
                                currentRun.GlyphInfoList.GlyphFlags[zwGlyphIx] = halantFlags;

                                _ZWJOffset = _lastHalantOffset; 
                            }
                            else if ( (ushort)(_ZWJOffset + 1) == _lastHalantOffset) 
                            { 
                                _lastHalantOffset = _ZWJOffset;
                            } 
                        }

                        if ((consonantFormFlags & IndicFormFlags.PostForm) != 0)
                        { 
                            _postOffset = _lastHalantOffset;
                            _consonantState = ConsonantState.AfterPost; 
                        } 
                        else
                        { 
                            _subOffset = _lastHalantOffset;
                            _consonantState = ConsonantState.AfterSub;
                        }
                    } 
                    else
                    { 
                        if (_halfOffset == _mainOffset && _mainOffset >= _lastMainOffset) 
                        {
                            // this is the second consonant (or maybe third, if 
                            // there's a reph) and the first was a half form
                            // so set this one as our main.
                           _mainOffset =  consonantCharOffset;
                        } 

                        // main is now HERE //* check for NoConjuncts? 
                        _lastMainOffset = consonantCharOffset; 
                    }
 
                    break;
                case ConsonantState.AfterSub:
                    if ((consonantFormFlags & IndicFormFlags.PostForm) != 0)
                    { 
                        _postOffset = _lastHalantOffset;
                        _consonantState = ConsonantState.AfterPost; 
                    } 
                    else if ((consonantFormFlags & (IndicFormFlags.SubForm | IndicFormFlags.VattuForm)) == 0)
                    { 
                        // a consonant with no shape class following a sub-base consonant.
                        // Hmmm!  Check a little further...

                        // The rules are that a prebase ra can come after a sub-form. 
                        // So if this is a prebase ra AND we haven't already marked an
                        // earlier ra as a prebase form, then we'll let this one past. 
                        // However, in all other cases, roll back our state... 
                        if ((consonantFormFlags & IndicFormFlags.PostbaseReph) == 0 || _postbaseRaOffset > 0)
                        { 
                            // roll everything back to main and set this as the last main,
                            // and hope font forms a conjunct.
                            _lastMainOffset = consonantCharOffset;
                            _subOffset = _postbaseRaOffset = 0; 
                            _consonantState = ConsonantState.AfterMain;
                        } 
                    } 
                    break;
                case ConsonantState.AfterPost: 
                    if ((consonantFormFlags & IndicFormFlags.PostForm) == 0)
                    {
                        // hmmm!  This isn't a post-form consonant.  Better check further
                        // (have to do something about this!) 

                        // The rules are that a prebase ra can come after a post-form. 
                        // So if this is a prebase ra AND we haven't already marked an 
                        // earlier ra as a prebase form, then we'll let this one past.
                        // However, in all other cases, roll back our state... 
                        if ((consonantFormFlags & IndicFormFlags.PostbaseReph) == 0 || _postbaseRaOffset > 0)
                        {
                            if ((consonantFormFlags & (IndicFormFlags.SubForm | IndicFormFlags.VattuForm)) != 0)
                            { 
                                // roll everything back to subs
                                // and hope font forms a conjunct. 
                                _lastMainOffset = _lastConsonantOffset; 
                                _subOffset = _lastHalantOffset;
                                _consonantState = ConsonantState.AfterSub; 
                            }
                            else
                            {
                                // a consonant with no shape class (maybe font'll do something 
                                // with it?); so roll everything back to main and set this
                                // as the last main, and hope for conjunct formations 
                                _lastMainOffset = consonantCharOffset; 
                                _consonantState = ConsonantState.AfterMain;
                            } 
                            _postOffset = _postbaseRaOffset = 0;   // clear. If appropriate, raoffset'll be reset below
                        }

                    } 
                    break;
            } 
 
            if (_mainOffset != consonantCharOffset)
            { 
                // take care of vattus.  If this consonant is not the reph or 1st
                // main consonant, check for vattu form
                if ((consonantFormFlags & IndicFormFlags.VattuForm) != 0)
                { 
                    _isVattuPresent = true;
                } 
 
                // -->
                // Special reordering ra handling... 
                if ((consonantFormFlags & IndicFormFlags.PostbaseReph) != 0 && _postbaseRaOffset == 0)
                {
                    // post-base RA glyph will get reordered to before the
                    // last main consonant.  So set the offset if this is a potential post main RA 
                    _isReorderingNecessary = true;
                    _postbaseRaOffset = _lastHalantOffset; 
                } 
                // <--
                // End special reordering ra handling... 
            }

            _lastConsonantOffset = consonantCharOffset;
            return true; 
        }
 
        private bool AppendHalant () 
        {
            bool succeeded = false; 

            if ( _isMatraPresent )
            {
                // This is a post-matra halant.  Check that there's not 
                // already one.
                if (!_isPostMatraHalantPresent) 
                { 
                    succeeded = _isPostMatraHalantPresent = true;
                } 
            }
            else if (!_isHalantActive)
            {
                // this is the first (only) halant since the last consonant (good!) 
                succeeded = _isHalantActive = true;
            } 
 
            return succeeded;
        } 

        /// 
        /// Critical - calls critical code
        ///  
        [SecurityCritical]
        private bool AppendNukta ( ref ShapingWorkspace currentRun, ushort nuktaCharOffset ) 
        { 
            bool succeeded = false;
            if ( _isMatraPresent ) 
            {
                // this nukta follows a matra, so add the matra's
                // positioning info to our shape info
                if (!_isPostMatraNuktaPresent) 
                {
                    // move this nukta to after the preceding (reordered) matra 
                    // reposition this matra 
                    IndicRepositioningClass nuktaReposClass = _lastMatraRepositioningClass;
                    ushort toPosition = GetRepositionOffsetAndUpdateCluster(nuktaReposClass, 
                                                                            false,  // not reph
                                                                            1);
                    RepositionCharacters(ref currentRun,
                                         (ushort)(nuktaCharOffset + _unmappedGlyphsCount), 
                                         toPosition,
                                         1); 
 
                    succeeded = _isPostMatraNuktaPresent = true;
                    _lastNuktaOffset = toPosition; 
                }


            } 
                // won't allow multiple nuktas for one consonant (ok, no more than two)
            else if (_lastNuktaOffset <= _lastConsonantOffset + 1) 
            { 
                // we may have to adjust the consonant state..
                if (_consonantState == ConsonantState.AfterReph) 
                {
                    // if this nukta is modifying what we thought was our
                    // reph, better change that
                    _isRephReorderPending = false; 
                    _mainOffset = _lastConsonantOffset;
                    _consonantState = ConsonantState.AfterMain; 
                } 
                else if (_consonantState == ConsonantState.AfterSub || _consonantState == ConsonantState.AfterPost)
                { 
                    // nukta not allowed on subs/posts
                    // roll back to main
                    _lastMainOffset = _lastConsonantOffset;
                    _consonantState = ConsonantState.AfterMain; 
                }
 
                succeeded = _isNuktaPresent = true; 
            }
 
            return succeeded;
        }

        private bool AppendZWJ ( ref ShapingWorkspace currentRun, ushort zwjCharOffset ) 
        {
            if (_isHalantActive) 
            { 
                if (_consonantState == ConsonantState.AfterReph &&
                    _scriptIx == IndicScriptIndices.Kannada) 
                {
                     // For compatibility with legacy useage in Kannada,
                     // Ra+h+ZWJ must behave like Ra+ZWJ+h...
                    _mainOffset = _lastConsonantOffset; 
                    _consonantState = ConsonantState.AfterMain;
                } 
                else 
                {
                    // this ZWJ follows a halant.  We expect that the following 
                    // consonant will be the main
                    _isHalantActive = false;
                    _isVattuPresent = false;
 
                    // treat this "C + H + ZWJ" as a half-form.
                    // (Malayalam has no half forms, but it does use this sequence 
                    // to specify the chillaksaram forms and we may want any pre-main 
                    // matras to go behind the chillaksaram (in ReorderGlyphs())
                    if (_halfOffset > _mainOffset) 
                    {
                        _halfOffset = _mainOffset;
                    }
 
                    _mainOffset = (ushort)(zwjCharOffset + 1);
                    _consonantState = ConsonantState.AfterZWJ; 
                } 

                // if this ZWJ is followin a RA+h that we thought might be 
                // a prebase form, now we know it isn't!
                if (_postbaseRaOffset == _lastConsonantOffset)
                {
                    _postbaseRaOffset = 0; 
                }
 
            } 
            else if ( _consonantState == ConsonantState.AfterReph)
            { 
               _mainOffset = _lastConsonantOffset;
               _consonantState = ConsonantState.AfterMain;
            }
 
            if (!_isZWJPresent)
            { 
                _isZWJPresent = true; 
                _ZWJOffset = zwjCharOffset;
            } 
            return true;
        }

        private bool AppendZWNJ (   ref ShapingWorkspace currentRun, ushort zwnjCharOffset ) 
        {
            if (_isHalantActive) 
            { 
                // ZWNJ following a C+H.
                _isHalantActive = false; 

                if (_postbaseRaOffset == 0 || _postbaseRaOffset < _lastConsonantOffset)
                {
                    _mainOffset = _lastConsonantOffset; 
                    _postbaseRaOffset = 0;
                } 
                else 
                {
                    // last consonant is our pre-base ra.  Mark it as "last main", too. 
                    _lastMainOffset = _lastConsonantOffset;
                }
            }
            else if (_scriptIx == IndicScriptIndices.Bengali) 
            {
                // ZWNJ following a ra (only for Bengali) 
                _mainOffset = _lastConsonantOffset; 
                _consonantState = ConsonantState.AfterMain;
            } 

            _isZWJPresent = true;
            _ZWJOffset = zwnjCharOffset;
            return true; 
        }
 
        ///  
        /// Critical - calls critical code
        ///  
        [SecurityCritical]
        private bool AppendMatra ( ref ShapingWorkspace currentRun, ushort matraCharOffset  )
        {
            bool succeeded = false; // assume the worst! 
            char matraChar = currentRun.CurrentChar;
            IndicFormFlags matraPositionFlags = _charConverter.ToFormInfo(matraChar); 
 
                // check that this matra has no positions already claimed by preceding matras
            if (matraPositionFlags > _matraPositionFlags) 
            {
                // keep track of first matra
                if (_matraPositionFlags == 0)
                { 
                    FinalizeOffsets(ref currentRun, matraCharOffset, false);
                } 
 
                // if this is a post-vowel matra, validate the matra
                if (_isVowelPresent && _vowelOffset == _lastConsonantOffset) 
                {
                    char[] invalidVowelList =
                        _charConverter.GetMatraInvalidVowelList(matraChar);
 
                    if (invalidVowelList != null)
                    { 
                        char vowelChar = currentRun.GetChar((ushort)(_firstCharIx + _vowelOffset)); 
                        for (int i = 0; i < invalidVowelList.Length; ++i)
                        { 
                            if (invalidVowelList[i] == vowelChar)
                            {
                                return false;   // this matra is not allowed with this vowel base
                            } 
                        }
                    } 
                } 

                _matraPositionFlags |= matraPositionFlags; 
                succeeded = true;
            }
            else
            { 
                // Seems this matra wants to position in the same place as a previous matra.
                // This is not allowed, except for a couple of Kannada exceptions 
                // (0xCCA+0xCD5 or 0xCC6+0xCC2+0xCD5) 
                if (matraChar == '\u0CD5')
                { 
                    // for Kannada script, there are three ways to type the oo vowel sign
                    // if this is one of those, we're happy (this is the only acceptable
                    // case of two matras with the same reordering position)
                    char prevChar = currentRun.PreviousChar; 
                    if (prevChar == '\u0CCA')
                    { 
                        succeeded = true; 
                    }
                    else if (prevChar == '\u0CC2') 
                    {
                        ushort matraCharIx = currentRun.CurrentCharIx;
                        if (matraCharIx >= _firstCharIx + 3 &&
                             currentRun.GetChar((ushort)(matraCharIx - 2)) == '\u0CC6') 
                        {
                            succeeded = true; 
                        } 
                    }
 
                }
            }

            if (succeeded) 
            {
                CharShapeInfo matraShapeInfo = _charConverter.ToShapeInfo(matraChar); 
                bool isSplitMatra = 
                    ((matraShapeInfo & IndicShape.ShapeIncludesPositioningInfoFlag) == 0);
                if (isSplitMatra) 
                {
                    // This is a split matra...so move the glyphs where they
                    // belong
                    RepositionSplitMatra(ref currentRun, matraCharOffset ); 
                }
                else 
                { 
                    // reposition this matra
                    _lastMatraRepositioningClass = GetRepositioningClass(matraShapeInfo); 
                    ushort toPosition = GetRepositionOffsetAndUpdateCluster(_lastMatraRepositioningClass,
                                                                            false,  // not reph
                                                                            1);
                    RepositionCharacters(ref currentRun, (ushort)(matraCharOffset + _unmappedGlyphsCount), toPosition, 1); 

                    // special case for Gurmukhi - when there's no post consonant but 
                    // there is an after-post matra, then move the reph after the 
                    // matra (unless its one of the two matras,  0xa3e or 0xa40)
                    if (_isRephReorderPending && _scriptIx == IndicScriptIndices.Gurmukhi) 
                    {
                        if (_lastMatraRepositioningClass == IndicRepositioningClass.AfterPost)
                        {
                            if (matraChar != '\u0a3e' && matraChar != '\u0a40') 
                            {
                               _gurmukhiRephGoesAfterPostMatra = true; 
                            } 
                        }
                    } 
                }
                _isMatraPresent = true;

            } 
            return succeeded;
        } 
 
        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical]
        private bool AppendVowelSign ( ref ShapingWorkspace currentRun, ushort vowelSignCharOffset  )
        { 
            bool succeeded = true;
            FinalizeOffsets( ref currentRun, vowelSignCharOffset, false ); 
 
            // a vedic is a vowel sign and is the only way we'd see multiple
            // vowel signs.  We can only have one of each vedic and only 
            // two vedics, so check if preceding character is same as this one
            // (that would be bad)
            char vowelSignChar = currentRun.CurrentChar;
            succeeded = (currentRun.PreviousChar != vowelSignChar); 
            if (succeeded)
            { 
                // for matras and vowel signs, reposition them now 
                IndicRepositioningClass signReposClass = _charConverter.ToRepositioningClass(vowelSignChar);
                ushort toPosition = GetRepositionOffsetAndUpdateCluster(signReposClass, 
                                                                        false,  // not reph
                                                                        1);
                RepositionCharacters(ref currentRun,
                                     (ushort)(vowelSignCharOffset + _unmappedGlyphsCount), 
                                     toPosition,
                                     1); 
            } 
            return succeeded || vowelSignCharOffset == 0;
        } 


        /// 
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        public void FinalizeOffsets ( ref ShapingWorkspace currentRun, ushort currentCharOffset, bool isFinalCall ) 
        {
            if (_lastMainOffset < _mainOffset) 
            {
                _lastMainOffset = _mainOffset;
            }
 
            if (currentCharOffset > 0)
            { 
 
               if (_consonantState != ConsonantState.EndOfClusterReached &&
                   _consonantState != ConsonantState.Start) 
                {
                    // if there's no half forms, set the half offset now to the first
                    // consonant (in case any matra wants to reposition to After Start)
                    if (_halfOffset > _mainOffset) 
                    {
                        _halfOffset = _mainOffset; 
                    } 

                    if ( !_useV2FontRules ) 
                    {

                        if ( _lastMainOffset + 2 < currentCharOffset)
                        { 
                            // for non-V2 script fonts (ie, pre-Vista Indic fonts) we
                            // need to effectively swap the post-main consonants and their 
                            // halants before applying the font features because these 
                            // fonts use C+h lookups rather than h+C lookups.
 
                            // find first halant
                            ushort halantOffset = (ushort)(_lastMainOffset + 1); // start here
                            ushort halantGlyphIx =
                                (ushort)(currentRun.GetGlyphIx(_firstCharIx) + halantOffset); 
                            bool foundHalant =
                                (_fontClient.HalantGlyph == currentRun.GetGlyph(halantGlyphIx)); 
                            if (!foundHalant && (_lastMainOffset + 3 < _clusterSize)) 
                            {
                                // didn't find a halant.  Maybe this is a ZWJ?  Try the next glyph 
                                foundHalant = (_fontClient.HalantGlyph == currentRun.GetGlyph(++halantGlyphIx));
                                ++halantOffset;
                            }
                            if (foundHalant) 
                            {
                                // so, we have our halant.  If there's a trailing halant, 
                                // move the first halant based on its repositioning class, 
                                // otherwise if there's a sub- or post- consonant, move
                                // past it 
                                ushort toPosition = halantOffset;
                                if (_isHalantActive)
                                {
                                     toPosition = 
                                        GetRepositionOffsetAndUpdateCluster(_charConverter.HalantRepositioningClass,
                                                                        false, 
                                                                        0); 
                                }
                                else if (_postOffset > 0 || _subOffset > 0) 
                                {
                                   toPosition = currentCharOffset;
                                }
 
                                RepositionCharacters(ref currentRun, halantOffset, toPosition, 1 );
 
                            } 

                        } 


                    }
 
                    _consonantState = ConsonantState.EndOfClusterReached;
                } 
 
                if (isFinalCall)
                { 
                    if (_isRephReorderPending)
                    {
                        // move the reph to where it belong...
                        IndicRepositioningClass rephPosition = 
                            _charConverter.ToRepositioningClass( currentRun.GetChar(_firstCharIx) );
                        ushort toPosition = GetRepositionOffsetAndUpdateCluster(rephPosition, 
                                                                                true,   // is reph 
                                                                                2);
 
                        RepositionCharacters(ref currentRun,0,toPosition,2);

                        if (_lastMainOffset == _mainOffset || (_postOffset > 0 && toPosition > _postOffset))
                        { 
                            _isRephReorderPending = false;
                        } 
                        else 
                        {
                            _isReorderingNecessary = true; 
                        }
                    }

                    if (_unmappedGlyphsCount > 0) 
                    {
                        UpdateSplitMatraClusterOffsets(ref currentRun); 
                    } 
                }
 
            }


        } 

        ///  
        /// Critical - calls critical code 
        /// 
        [SecurityCritical] 
        private void UpdateSplitMatraClusterOffsets(ref ShapingWorkspace currentRun)
        {
            // Split matra components mean that the char:glyph offset relationship
            // is not 1:1 for this cluster.  We have already moved all the matras 
            // to their places and need to make some adjustments before we try
            // to have OTLS apply the font features.  Our strategy will be to 
            // try to map 2 glyphs to 1 character till we've compensated for 
            // all the matra component glyphs added by...
            //      first, if there's a reph map it to one character 
            //      second, if there's a prebase ra, map it to one character
            //      third, if there's a sub form consonant, map the halant-consonant
            //              pair to one glyph
            //      fourth, do the same if there's a post form consonant 
            //      fifth, if more than one matra are adjacent to each other, map
            //              them to one glyph 
            //      sixth, if we've still not brought our "virtual" character 
            //              count back to par and we have a prebase matra, map
            //              the following glyph to the same character as the 
            //              matra
            //      finally, if all else fails map the remaining "extra" glyphs
            //              to the same character as the last glyph with a valid
            //              FirstChars value. 
            GlyphInfoList glyphs = currentRun.GlyphInfoList;
            UshortList charMap = currentRun.CharMap; 
            ushort firstGlyphInCluster = currentRun.GetGlyphIx(_firstCharIx); 
            ushort lastGlyphInCluster =
                (ushort)(firstGlyphInCluster + _clusterSize + _unmappedGlyphsCount - 1); 
            ushort lastCharInCluster =
                (ushort)(_firstCharIx + _clusterSize - 1);

            // first reset the first chars and charmap entries to 1:1 glyphIx to charIx 
            // when this is done, the extra glyphs will all refer to character indices that
            // are invalid (beyond the actual end of the character range).  Not to worry! 
            // We're gonna fix it up... 
            ushort nextGlyphIx = firstGlyphInCluster;
            ushort nextCharIx = _firstCharIx; 
            while (nextCharIx <= lastCharInCluster)
            {
                glyphs.FirstChars[nextGlyphIx] = nextCharIx;
                charMap[nextCharIx++] = nextGlyphIx++; 
            }
            while (nextGlyphIx <= lastGlyphInCluster) 
            { 
                glyphs.FirstChars[nextGlyphIx++] = nextCharIx++;
            } 

            // now look for possible chars to "squeeze out" so that our glyph to character
            // mapping gets in [....]
            ushort passId = 0; 
            while (_unmappedGlyphsCount > 0)
            { 
                bool isSqueezeTarget = false; 
                ushort clusterCharOffset = 0;
                switch(passId++) 
                {
                    case 0:
                        if (_isPostMatraNuktaPresent &&
                            _lastNuktaOffset > 0 && _lastNuktaOffset < _mainOffset) 
                        {
                            // compress the matra+nukta to one character 
                            clusterCharOffset = _preMainMatraOffset; 
                            _lastNuktaOffset = _preMainMatraOffset;
                            isSqueezeTarget = true; 
                        }
                        break;
                    case 1:
                        if (_postbaseRaOffset > 0) 
                        {
                            // compress the prebase ra to one character 
                            clusterCharOffset = _postbaseRaOffset; 
                            isSqueezeTarget = true;
 
                            if (_lastMainOffset == (ushort)(_postbaseRaOffset + 1))
                            {
                                _lastMainOffset = _postbaseRaOffset;
                            } 
                        }
                        break; 
                    case 2: 
                        if (_rephOffset > 0)
                        { 
                            // compress the reph to one character
                            clusterCharOffset = _rephOffset;
                            isSqueezeTarget = true;
                        } 
                        break;
                    case 3: 
                        if (_subOffset > 0 && _postbaseRaOffset != _subOffset) 
                        {
                            // compress the (first) sub-base to one character 
                            clusterCharOffset = _subOffset;
                            isSqueezeTarget = true;
                        }
                        break; 
                    case 4:
                        if (_postOffset > 0) 
                        { 
                            // compress the (first) post-base to one character
                            clusterCharOffset = _postOffset; 
                            isSqueezeTarget = true;
                        }
                        break;
                    case 5: 
                        if (_isPostMatraNuktaPresent && _lastNuktaOffset > _mainOffset)
                        { 
                            // compress the matra+nukta to one character 
                            clusterCharOffset = (ushort)(_lastNuktaOffset - 1);
                            isSqueezeTarget = true; 
                        }
                        break;
                    case 6:
                        if ( _lastMainOffset > _mainOffset ) 
                        {
                            // compress the first base with the first character that follows it 
                            // (could be nukta, halant, ZWJ) to one character 
                            clusterCharOffset = _mainOffset;
                            isSqueezeTarget = true; 
                        }
                        break;
                    default:
                        // If we're here none (or not enough) of the possible 2 glyphs to 1 character 
                        // mapping assignments has been enough.  So just map all of the extra glyphs
                        // to the end of the cluster.  Be careful to check if the reph is at the end; 
                        // if so, adjust the reph to be the last char 
                        bool isRephAtEnd = _rephOffset >= lastCharInCluster;
 
                        nextGlyphIx = lastGlyphInCluster;
                        while (nextGlyphIx > firstGlyphInCluster &&
                               glyphs.FirstChars[nextGlyphIx] >= lastCharInCluster)
                        { 
                            glyphs.FirstChars[nextGlyphIx--] = lastCharInCluster;
                            if (isRephAtEnd) 
                            { 
                                _rephOffset = lastCharInCluster--;
                                isRephAtEnd = false; 
                            }
                        }
                        _unmappedGlyphsCount = 0;
                        break; 

                } 
 
                if (isSqueezeTarget)
                { 
                    if (_lastMainOffset > 0 && _lastMainOffset > clusterCharOffset) --_lastMainOffset;
                    if (_subOffset > 0 && _subOffset > clusterCharOffset) --_subOffset;
                    if (_postOffset > 0 && _postOffset > clusterCharOffset) --_postOffset;
                    if (_rephOffset > 0 && _rephOffset > clusterCharOffset) --_rephOffset; 
                    if (_postbaseRaOffset > 0 && _postbaseRaOffset > clusterCharOffset) --_postbaseRaOffset;
 
                    // now update the glyph firstchars.  The second glyph must now point to the 
                    // same character as the first glyph, so update all the remaining entries
                    // (subtract one from the enntry) 
                    nextGlyphIx = (ushort)(firstGlyphInCluster + clusterCharOffset);
                    while (++nextGlyphIx <= lastGlyphInCluster)
                    {
                        glyphs.FirstChars[nextGlyphIx] -= 1; 
                    }
 
                    _unmappedGlyphsCount -= 1; 
                }
            } 

            // Now we need to fix up the charmap.  Some of the characters may map to two
            // glyphs.  Find them, starting from the beginning of the cluster.  For each
            // pair of glyphs mapped to one character, adjust all the following charmap 
            // entries (add one to charmap entry)
            nextCharIx = _firstCharIx; 
            nextGlyphIx = firstGlyphInCluster; 
            ushort charsInRun = currentRun.CharsCount;
            while (nextCharIx <= lastCharInCluster) 
            {
                if (nextGlyphIx < lastGlyphInCluster &&
                    glyphs.FirstChars[nextGlyphIx + 1] == glyphs.FirstChars[nextGlyphIx])
                { 
                    ushort charMapIx = nextCharIx;
                    while (++charMapIx <= lastCharInCluster) 
                    { 
                        charMap[charMapIx] += 1;
                    } 

                    ++nextGlyphIx;
                }
                ++nextCharIx; 
                ++nextGlyphIx;
            } 
 
        }
 
        /// 
        /// Critical - calls critical code
        /// 
        [SecurityCritical] 
        private bool FindLateRepositionAnchor(ref ShapingWorkspace currentRun)
        { 
            bool foundAnchor = false; 
            ushort newLastMainOffset = _lastMainOffset;
 
            if (_mainOffset < _lastMainOffset)
            {
                // check if conjunct formed
                ushort mainGlyph = currentRun.GetGlyphIx((ushort)(_firstCharIx + _mainOffset)); 
                ushort nextGlyphToTest = currentRun.GetGlyphIx((ushort)(_firstCharIx + _lastMainOffset));
 
                if (mainGlyph < nextGlyphToTest) 
                {
                    // conjunct didn't form.  If looking for matra anchor, break on first explicit 
                    // halant.  Otherwise, keep looking for an earlier halant (main might be
                    // C+h+C+h+C).
                    ushort halantGlyphIx = nextGlyphToTest;
                    ushort halantGlyph = _fontClient.HalantGlyph; 
                    while (--nextGlyphToTest > mainGlyph)
                    { 
                        if (halantGlyph == currentRun.GetGlyph(nextGlyphToTest)) 
                        {
                            // found it.  Set main offset to point to the consonant just past here. 
                            ushort halantCharIx = currentRun.GlyphInfoList.FirstChars[nextGlyphToTest];
                            halantGlyphIx = nextGlyphToTest;
                            newLastMainOffset = (ushort)(halantCharIx + 1 - _firstCharIx);
 
                            if (_isSplitMatraPresent)
                            { 
                                // it may be that we mapped the main+halant glyphs to one character 
                                // because of added split matra component glyphs.  We need to check
                                // and if this is the case we need to increment newLastMainOffset 
                                if (currentRun.GlyphInfoList.FirstChars[nextGlyphToTest - 1] == halantCharIx)
                                {
                                    ++newLastMainOffset;
                                } 
                            }
 
                            foundAnchor = true;     // 

                            if(_isMatraReorderPending || _postbaseRaOffset > 0) 
                            {
                                break;
                            }
                        } 
                    }
 
                    // not quite done yet.  If there's a ZWJ/NJ right after (either of) the halant 
                    // we need to move past this
                    if (foundAnchor) 
                    {

                        if (_isZWJPresent)
                        { 
                            if((currentRun.GlyphInfoList.GlyphFlags[halantGlyphIx] & (ushort)GlyphFlags.ZeroWidth)
                                                != 0) 
                            { 
                               ++newLastMainOffset;
                            } 
                        }

                        // Also check if font has set the proposed new anchor points as a mark
                        // (some font mark sub-base consonants this way).  If this is so, we 
                        // don't want to move anything here....
                        if ((currentRun.GlyphInfoList.GlyphFlags[currentRun.GetGlyphIx(newLastMainOffset)] 
                                    & (ushort)GlyphFlags.GlyphTypeMask) 
                                                == (ushort)GlyphFlags.Mark)
                        { 
                            foundAnchor = false;     //
                        }
                    }
 

                } 
 
            }
 
            if (!foundAnchor && _scriptIx == IndicScriptIndices.Malayalam &&
                _isMatraReorderPending &&
                _isZWJPresent)
            { 
                // special Mayalayam case - if we have a chillaksaram that has
                // not become part of a composite glyph, we want to set our 
                // anchor to after the chillaksaram 
                // 
                ushort mainCharOffset = _postbaseRaOffset > 0 ? 
                                     (ushort)(_postbaseRaOffset - 1) : _lastMainOffset;
                if (mainCharOffset < _clusterSize)
                {
                    ushort halfFormGlyphIx = currentRun.GetGlyphIx((ushort)(_firstCharIx + _halfOffset)); 
                    ushort nextGlyphToTest = currentRun.GetGlyphIx((ushort)(_firstCharIx + _mainOffset));
                    ushort mainGlyph = currentRun.GetGlyphIx((ushort)(_firstCharIx + mainCharOffset)); 
                    while (nextGlyphToTest <= mainGlyph) 
                    {
                        if (nextGlyphToTest != halfFormGlyphIx && 
                           (currentRun.GlyphInfoList.GlyphFlags[nextGlyphToTest] & (ushort)GlyphFlags.GlyphTypeMask)
                                        != (ushort)GlyphFlags.Mark)
                        {
                            foundAnchor = true;     // 
                            newLastMainOffset = (ushort)(currentRun.GlyphInfoList.FirstChars[nextGlyphToTest] - _firstCharIx);
                            break; 
                        } 
                        ++nextGlyphToTest;
                    } 
                }
            }
            _lastMainOffset = foundAnchor ? newLastMainOffset : _mainOffset;
            return foundAnchor; 
        }
 
        private static IndicRepositioningClass GetRepositioningClass(CharShapeInfo charShape) 
        {
            Debug.Assert((charShape & (CharShapeInfo) IndicCharClass.IncludesPositioningInfo) != 0,"\"Includes Positioning Info\" flag not set."); 
            charShape = (CharShapeInfo)((ushort)charShape >> IndicShape.RepositioningInfoShift);
            return (IndicRepositioningClass)charShape;
        }
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK