private unsafe LsErr ExpandGlyphs(
            LsGlyphRunInfo      *plsglyphrunInfo,
            int                 interWordExpandTo,
            int                 **pplsExpansionLeft,
            int                 **pplsExpansionRight,
            LsExpType           *plsexptype,
            LsExpType           interWordExpansionType,
            LsExpType           interLetterExpansionType
            )
        {
            char* pwch = plsglyphrunInfo->pwch;
            ushort* pgmap = plsglyphrunInfo->rggmap;
            int cchRun = plsglyphrunInfo->cwch;
            int cgiRun = plsglyphrunInfo->cgindex;

            int ich = 0;
            int igi = pgmap[ich];
            int cgi = 0;


            while (ich < cchRun)
            {

                int cch = 1;
                while (ich + cch < cchRun && pgmap[ich + cch] == igi)
                    cch++;


                cgi = (ich + cch == cchRun) ? cgiRun - igi : pgmap[ich + cch] - igi;

                int i, j;


                for (j = 0; j < cch; j++)
                {
                    ushort flags = (ushort)(Classification.CharAttributeOf((int)Classification.GetUnicodeClassUTF16(pwch[ich + j]))).Flags;
                    if ((flags & ((ushort)CharacterAttributeFlags.CharacterSpace)) != 0)
                        break;
                }

                int glyphAdvance = 0;
                for (i = 0; i < cgi; i++)
                {
                    glyphAdvance += plsglyphrunInfo->rgduWidth[igi + i];


                    pplsExpansionLeft[0][igi + i] = 0;
                    pplsExpansionLeft[1][igi + i] = 0;
                    pplsExpansionLeft[2][igi + i] = 0;


                    pplsExpansionRight[0][igi + i] = 0;
                    pplsExpansionRight[1][igi + i] = 0;
                    pplsExpansionRight[2][igi + i] = 0;

                    if (i == cgi - 1)
                    {
                        if (cch == 1 && j < cch)
                        {

                            int expandedBy = Math.Max(0, interWordExpandTo - glyphAdvance);
                            pplsExpansionRight[0][igi + i] = expandedBy;
                            pplsExpansionRight[1][igi + i] = expandedBy * Constants.AcceptableLineStretchability;
                            pplsExpansionRight[2][igi + i] = FullText.FormatWidth;
                            plsexptype[igi + i] = interWordExpansionType;
                        }
                        else
                        {

                            pplsExpansionRight[2][igi + i] = FullText.FormatWidth;
                            plsexptype[igi + i] = interLetterExpansionType;
                        }
                    }
                }

                ich += cch;
                igi += cgi;
            }

            Invariant.Assert(igi == cgiRun);
            return LsErr.None;
        }
        private unsafe LsErr CompressGlyphs(
            LsGlyphRunInfo      *plsglyphrunInfo,
            int                 interWordCompressTo,
            int                 **pplsCompressionLeft,
            int                 **pplsCompressionRight
            )
        {
            char* pwch = plsglyphrunInfo->pwch;
            ushort* pgmap = plsglyphrunInfo->rggmap;
            int cchRun = plsglyphrunInfo->cwch;
            int cgiRun = plsglyphrunInfo->cgindex;

            int ich = 0;
            int igi = pgmap[ich];
            int cgi = 0;


            while (ich < cchRun)
            {

                int cch = 1;
                while (ich + cch < cchRun && pgmap[ich + cch] == igi)
                    cch++;


                cgi = (ich + cch == cchRun) ? cgiRun - igi : pgmap[ich + cch] - igi;

                int i, j;


                for (j = 0; j < cch; j++)
                {
                    ushort flags = (ushort)(Classification.CharAttributeOf((int)Classification.GetUnicodeClassUTF16(pwch[ich + j]))).Flags;
                    if ((flags & ((ushort)CharacterAttributeFlags.CharacterSpace)) != 0)
                        break;
                }

                int glyphAdvance = 0;
                for (i = 0; i < cgi; i++)
                {
                    glyphAdvance += plsglyphrunInfo->rgduWidth[igi + i];


                    pplsCompressionLeft[0][igi + i] = 0;
                    pplsCompressionLeft[1][igi + i] = 0;
                    pplsCompressionLeft[2][igi + i] = 0;


                    pplsCompressionRight[0][igi + i] = 0;
                    pplsCompressionRight[1][igi + i] = 0;
                    pplsCompressionRight[2][igi + i] = 0;

                    if (    i == cgi - 1
                        &&  cch == 1
                        &&  j < cch
                        )
                    {

                        pplsCompressionRight[0][igi + i] = Math.Max(0, glyphAdvance - interWordCompressTo);
                    }
                }

                ich += cch;
                igi += cgi;
            }

            Invariant.Assert(igi == cgiRun);
            return LsErr.None;
        }
        internal unsafe LsErr GetGlyphExpansionInfoFullMixed(
            IntPtr              pols,                   // Line Layout context
            LsDevice            device,                 // kind of device
            LsTFlow             textFlow,               // text flow
            LsGlyphRunInfo      *plsglyphrunInfo,       // glyph-based run info
            LsNeighborInfo      *plsneighborInfoLeft,   // left neighbor info
            LsNeighborInfo      *plsneighborInfoRight,  // right neigbor info
            int                 maxPriorityLevel,       // maximum priority level
            int                 **pplsexpansionLeft,    // [in/out] fill in left expansion amount per priority level on the way out
            int                 **pplsexpansionRight,   // [in/out] fill in right expansion amount per priority level on the way out
            LsExpType           *plsexptype,            // [in/out] fill in glyph expansion type for each glyph
            int                 *pduMinInk              // [in/out] fill in glyph minimum expansion for exptAddInkContinuous
            )
        {
            LsErr lserr = LsErr.None;
            Plsrun plsrun = Plsrun.Undefined;
            LSRun lsrun = null;

            try
            {
                Invariant.Assert(maxPriorityLevel == 3);

                plsrun = plsglyphrunInfo->plsrun;
                lsrun = FullText.StoreFrom(plsrun).GetRun(plsrun);
                int em = lsrun.EmSize;

                return ExpandGlyphs(
                    plsglyphrunInfo,
                    (int)(em * Constants.MaxInterWordExpansionPerEm),
                    pplsexpansionLeft,
                    pplsexpansionRight,
                    plsexptype,
                    LsExpType.AddWhiteSpace,    // inter-word expansion type


                    ((lsrun.BidiLevel & 1) == 0 ? LsExpType.AddWhiteSpace : LsExpType.None)
                    );
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetGlyphExpansionInfoFullMixed", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal unsafe LsErr GetGlyphCompressionInfoFullMixed(
            IntPtr              pols,                   // Line Layout context
            LsDevice            device,                 // kind of device
            LsTFlow             textFlow,               // text flow
            LsGlyphRunInfo      *plsglyphrunInfo,       // glyph-based run info
            LsNeighborInfo      *plsneighborInfoLeft,   // left neighbor info
            LsNeighborInfo      *plsneighborInfoRight,  // right neigbor info
            int                 maxPriorityLevel,       // maximum priority level
            int                 **pplscompressionLeft,  // [in/out] fill in left compression amount per priority level on the way out
            int                 **pplscompressionRight  // [in/out] fill in right compression amount per priority level on the way out
            )
        {
            LsErr lserr = LsErr.None;
            Plsrun plsrun = Plsrun.Undefined;
            LSRun lsrun = null;

            try
            {
                Invariant.Assert(maxPriorityLevel == 3);

                plsrun = plsglyphrunInfo->plsrun;
                lsrun = FullText.StoreFrom(plsrun).GetRun(plsrun);
                int em = lsrun.EmSize;

                return CompressGlyphs(
                    plsglyphrunInfo,
                    (int)(em * Constants.MinInterWordCompressionPerEm),
                    pplscompressionLeft,
                    pplscompressionRight
                    );
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetGlyphCompressionInfoFullMixed", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }