internal unsafe LsErr EnumTab(
            IntPtr              pols,               // pointer to context
            Plsrun              plsrun,             // plsrun
            int                 cpFirst,            // first cp of the dnode run
            char                *pwchText,          // a single tab character
            char                tabLeader,          // a single tab leader character
            LsTFlow             lstFlow,            // flow direction
            int                 fReverseOrder,      // flag for reverse order enumeration
            int                 fGeometryProvided,  // flag for providing geometry information
            ref LSPOINT         pptStart,           // [in] logical start of the run (iff geometryProvided)
            ref LsHeights       heights,            // [in] height (iff geometryProvided)
            int                 dupRun              // width of the run
            )
        {       
            if (cpFirst < 0)
            {

                return LsErr.None;
            }
        
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try 
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);
                GlyphRun glyphRun = null;                
                
                if (lsrun.Type == Plsrun.Text)
                {         


                    int charWidth = 0;
                    lsrun.Shapeable.GetAdvanceWidthsUnshaped(
                        &tabLeader, 
                        1,
                        TextFormatterImp.ToIdeal, 
                        &charWidth
                        );                

                    glyphRun = ComputeUnshapedGlyphRun(
                        lsrun, 
                        lstFlow,
                        currentLine.Formatter,
                        false,      // glyph run origin not provided at enumeration time
                        pptStart, 
                        charWidth, 
                        1, 
                        &tabLeader, 
                        &charWidth,
                        currentLine.IsJustified
                        );

                }

                if (glyphRun != null)
                {                    
                    IndexedGlyphRuns.Add(
                        new IndexedGlyphRun(
                           currentLine.GetExternalCp(cpFirst),
                           1,       // dcp is 1 for a Tab character
                           glyphRun
                           )
                    );                    
                }                
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("EnumTab", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }

            return lserr;
        }
        internal LsErr InlineDraw(
            System.IntPtr   pols,           // Line Layout context
            Plsrun          plsrun,         // plsrun
            ref LSPOINT     ptRun,          // [in] pen position at which to render the object
            LsTFlow         textFlow,       // text flow direction
            int             runWidth        // object width
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                LSPOINT lsrunOrigin = ptRun;

                Debug.Assert(lsrun.Type == Plsrun.InlineObject);

                int baseDirection = currentLine.RightToLeft ? 1 : 0;
                int runDirection = (int)(lsrun.BidiLevel & 1);

                if (baseDirection != 0)
                {
                    lsrunOrigin.x = -lsrunOrigin.x;
                }

                TextEmbeddedObject textObject = lsrun.TextRun as TextEmbeddedObject;

                Debug.Assert(textObject != null);
                Debug.Assert(textFlow != LsTFlow.lstflowWS || runDirection != 0);

                if ((baseDirection ^ runDirection) != 0)
                {

                    lsrunOrigin.x -= runWidth;
                }


                Point baselineOrigin = new Point(
                    currentLine.Formatter.IdealToReal(currentLine.LSLineUToParagraphU(lsrunOrigin.x))+ Draw.VectorToLineOrigin.X,
                    currentLine.Formatter.IdealToReal((lsrunOrigin.y + lsrun.BaselineMoveOffset)) + Draw.VectorToLineOrigin.Y
                    );


                Rect objectBounds = textObject.ComputeBoundingBox(
                     baseDirection != 0, // rightToLeft
                    false  // no sideway support yet
                    );

                if (!objectBounds.IsEmpty)
                {


                    objectBounds.X += baselineOrigin.X;
                    objectBounds.Y += baselineOrigin.Y;
                }


                _boundingBox.Union(
                    new Rect(

                    LSRun.UVToXY(
                            Draw.LineOrigin,
                            new Point(),
                            objectBounds.Location.X,
                            objectBounds.Location.Y,
                            currentLine
                            ),

                    LSRun.UVToXY(
                            Draw.LineOrigin,
                            new Point(),
                            objectBounds.Location.X + objectBounds.Size.Width,
                            objectBounds.Location.Y + objectBounds.Size.Height,
                            currentLine
                            )
                        )
                    );

                DrawingContext drawingContext = Draw.DrawingContext;                
                
                if (drawingContext != null)
                {

                    Draw.SetGuidelineY(baselineOrigin.Y);

                    try 
                    {                    
                        if (Draw.AntiInversion == null)
                        {

                            textObject.Draw(
                                drawingContext,
                                LSRun.UVToXY(
                                    Draw.LineOrigin,
                                    new Point(),
                                    baselineOrigin.X,
                                    baselineOrigin.Y,
                                    currentLine
                                    ),
                                baseDirection != 0,
                                false
                                );
                        }
                        else
                        {




                            drawingContext.PushTransform(Draw.AntiInversion);
                            try 
                            {
                                textObject.Draw(drawingContext, baselineOrigin, baseDirection != 0, false);
                            } 
                            finally
                            {
                                drawingContext.Pop();
                            }
                        }
                    }
                    finally 
                    {
                        Draw.UnsetGuidelineY();
                    }
                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("InlineDraw", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal unsafe LsErr EnumText(        
            IntPtr                      pols,                           // ls context
            Plsrun                      plsrun,                         // plsrun
            int                         cpFirst,                        // first cp of the ls dnode
            int                         dcp,                            // dcp of the dnode
            char                        *pwchText,                      // characters for glyph run
            int                         cchText,                        // length of characters 
            LsTFlow                     lstFlow,                        // flow direction
            int                         fReverseOrder,                  // flag for reverse order enumeration
            int                         fGeometryProvided,              // flag for providing geometry 
            ref LSPOINT                 pptStart,                       // [in] logical start of the run
            ref LsHeights               pheights,                       // [in] height (iff geometryProvided)
            int                         dupRun,                         // width of the run
            int                         glyphBaseRun,                   // flag for glyph based run
            int                         *piCharAdvances,                // character advance widths (iff !glyphBaseRun)
            ushort                      *puClusterMap,                  // cluster map (iff glyphBaseRun)
            ushort                      *characterProperties,           // character properties (iff glyphBaseRun)
            ushort                      *puGlyphs,                      // glyph indices (iff glyphBaseRun)
            int                         *piJustifiedGlyphAdvances,      // glyph advances (iff glyphBaseRun)
            GlyphOffset                 *piiGlyphOffsets,               // glyph offsets (iff glyphBaseRun)
            uint                        *piGlyphProperties,             // glyph properties (iff glyphProperties)
            int                         glyphCount                      // glyph count
            )
        {
            Debug.Assert(fGeometryProvided == 0, "Line enumeration doesn't need geometry information");

            if (cpFirst < 0)
            {

                return LsErr.None;
            }
            
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try                 
            {   
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);
                GlyphRun glyphRun = null;
                if (glyphBaseRun != 0)
                {

                    if (glyphCount > 0)
                    {

                        glyphRun = ComputeShapedGlyphRun(
                            lsrun, 
                            currentLine.Formatter,
                            false,      // glyph run origin not provided
                            pptStart, 
                            cchText, 
                            pwchText, 
                            puClusterMap, 
                            glyphCount, 
                            puGlyphs,
                            piJustifiedGlyphAdvances, 
                            piiGlyphOffsets,
                            currentLine.IsJustified
                            );
                    }
                }
                else if (cchText > 0)
                {

                    dupRun = 0;
                    for (int i = 0; i < cchText; i++)
                    {
                        dupRun += piCharAdvances[i];
                    }

                    

                    glyphRun = ComputeUnshapedGlyphRun(
                        lsrun, 
                        lstFlow, 
                        currentLine.Formatter,
                        false,      // glyph run origin not provided at enumeration
                        pptStart, 
                        dupRun, 
                        cchText, 
                        pwchText, 
                        piCharAdvances,
                        currentLine.IsJustified
                        );
                }
                
                if (glyphRun != null)
                {





                    IndexedGlyphRuns.Add(
                        new IndexedGlyphRun(
                           currentLine.GetExternalCp(cpFirst),
                           dcp,
                           glyphRun
                           )
                    );
                }                   
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("EnumText", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            
            return lserr;
        }
        internal LsErr DrawStrikethrough(
            IntPtr          pols,           // Line Layout context
            Plsrun          plsrun,         // plsrun
            uint            stType,         // kind of strike
            ref LSPOINT     ptOrigin,       // [in] drawing origin
            int             stLength,       // strike length
            int             stThickness,    // strike thickness
            LsTFlow         textFlow,       // text flow direction
            uint            displayMode,    // display mode
            ref LSRECT      clipRect        // [in] clipping rectangle
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;
            try
            {
                if (!TextStore.IsContent(plsrun))
                {

                    return LsErr.None;
                }

                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                double strikeThroughPositionInEm;
                double strikeThroughThicknessInEm;

                GetLSRunStrikethroughMetrics(lsrun, out strikeThroughPositionInEm, out strikeThroughThicknessInEm);

                int baselineTop = ptOrigin.y + (int)Math.Round(lsrun.EmSize * strikeThroughPositionInEm);
                int overlineTop = baselineTop - (lsrun.BaselineOffset - (int)Math.Round(lsrun.EmSize * strikeThroughThicknessInEm));

                const uint locationMask = 
                    (1U << (int)TextDecorationLocation.OverLine) |
                    (1U << (int)TextDecorationLocation.Strikethrough) |
                    (1U << (int)TextDecorationLocation.Baseline);

                DrawTextDecorations(
                    lsrun,
                    locationMask,
                    ptOrigin.x,   // left
                    0,            // underline top; not used
                    overlineTop,
                    ptOrigin.y,   // strikethrough top from LS
                    baselineTop,
                    stLength,
                    stThickness,
                    textFlow
                    );
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawStrikethrough", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        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 GetRunCharWidths(
            IntPtr          pols,               // Line Layout context
            Plsrun          plsrun,             // plsrun
            LsDevice        device,             // kind of device
            char*           charString,         // character string
            int             stringLength,       // string length
            int             maxWidth,           // max width allowance
            LsTFlow         textFlow,           // text flow
            int*            charWidths,         // [out] returning char widths up to given upperbound
            ref int         totalWidth,         // [out] total run width
            ref int         stringLengthFitted  // [out] number of char fitted
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextFormatterImp formatter;

                if (FullText != null)
                {
                    lsrun = FullText.StoreFrom(plsrun).GetRun(plsrun);
                    formatter = FullText.Formatter;
                }
                else
                {








                    #if DEBUG



                    Debug.Assert(Draw.CurrentLine.FullTextState != null);
                    #endif
                    lsrun = Draw.CurrentLine.GetRun(plsrun);
                    formatter = Draw.CurrentLine.Formatter;
                }

                if (lsrun.Type == Plsrun.Text)
                {
                    Debug.Assert(lsrun.Shapeable != null && stringLength > 0);
                    lsrun.Shapeable.GetAdvanceWidthsUnshaped(charString, stringLength, TextFormatterImp.ToIdeal, charWidths);

                    totalWidth = 0;
                    stringLengthFitted = 0;

                    do
                    {
                        totalWidth += charWidths[stringLengthFitted];

                    } while (
                            ++stringLengthFitted < stringLength
                        && totalWidth <= maxWidth
                        );

                    if (totalWidth <= maxWidth && FullText != null)
                    {
                        int cpLimit = lsrun.OffsetToFirstCp + stringLengthFitted;
                        if (cpLimit > FullText.CpMeasured)
                        {
                            FullText.CpMeasured = cpLimit;
                        }
                    }
                }
                else
                {

                    charWidths[0] = 0;
                    totalWidth = 0;
                    stringLengthFitted = stringLength;
                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetRunCharWidths", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal LsErr GetRunUnderlineInfo(
            IntPtr          pols,           // Line Layout context
            Plsrun          plsrun,         // plsrun
            ref LsHeights   lsHeights,      // run height
            LsTFlow         textFlow,       // text flow direction
            ref LsULInfo    ulInfo          // [out] result underline info
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                lsrun = Draw.CurrentLine.GetRun(plsrun);

                Debug.Assert(
                    !TextStore.IsContent(plsrun) || lsrun.Type == Plsrun.Text || lsrun.Type == Plsrun.InlineObject,
                    "Invalid run"
                    );

                ulInfo = new LsULInfo();

                double underlinePositionInEm;
                double underlineThicknessInEm;

                if (lsrun.Shapeable != null)
                {
                    underlinePositionInEm = lsrun.Shapeable.UnderlinePosition;
                    underlineThicknessInEm = lsrun.Shapeable.UnderlineThickness;
                }
                else
                {

                    underlinePositionInEm = lsrun.RunProp.Typeface.UnderlinePosition;
                    underlineThicknessInEm = lsrun.RunProp.Typeface.UnderlineThickness;
                }

                ulInfo.cNumberOfLines = 1;
                ulInfo.dvpFirstUnderlineOffset = (int)Math.Round(lsrun.EmSize * -underlinePositionInEm);
                ulInfo.dvpFirstUnderlineSize = (int)Math.Round(lsrun.EmSize * underlineThicknessInEm);






                Debug.Assert(ulInfo.dvpFirstUnderlineSize >= 0);

                if (ulInfo.dvpFirstUnderlineSize <= 0)
                    ulInfo.dvpFirstUnderlineSize = 1;
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetAutoNumberInfo", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal LsErr FInterruptShaping(
            IntPtr          pols,               // Line Layout context
            LsTFlow         textFlow,           // text flow
            Plsrun          plsrunFirst,        // first run
            Plsrun          plsrunSecond,       // second run
            ref int         fIsInterruptOk      // [out] disconnect glyphs between runs?
            )
        {
            LsErr lserr = LsErr.None;

            try
            {
                TextStore store = FullText.StoreFrom(plsrunFirst);

                if (    !TextStore.IsContent(plsrunFirst)
                    ||  !TextStore.IsContent(plsrunSecond))
                {
                    fIsInterruptOk = 1;
                    return LsErr.None;
                }


                LSRun lsrunFirst = store.GetRun(plsrunFirst);
                LSRun lsrunSecond = store.GetRun(plsrunSecond);


                fIsInterruptOk = !(

                    lsrunFirst.BidiLevel == lsrunSecond.BidiLevel

                    && lsrunFirst.Shapeable != null
                    && lsrunSecond.Shapeable != null
                    && lsrunFirst.Shapeable.CanShapeTogether(lsrunSecond.Shapeable)
                    ) ? 1 : 0;

            }
            catch (Exception e)
            {
                SaveException(e, Plsrun.Undefined, null);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("FInterruptShaping", Plsrun.Undefined, null);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal unsafe LsErr GetGlyphsRedefined(
            IntPtr                      pols,                   // Line Layout context
            IntPtr*                     plsplsruns,             // array of plsruns
            int*                        pcchPlsrun,             // array of character count per run
            int                         plsrunCount,            // number of runs
            char*                       pwchText,               // character string
            int                         cchText,                // character count
            LsTFlow                     textFlow,               // text flow direction
            ushort*                     puGlyphsBuffer,         // [in/out] fixed-size buffer for glyph indices
            uint*                       piGlyphPropsBuffer,     // [in/out] fixed-size buffer for glyph properties list
            int                         cgiGlyphBuffers,        // glyph buffers length in glyphs
            ref int                     fIsGlyphBuffersUsed,    // [out] Boolean flag indicates glyph buffers being used
            ushort*                     puClusterMap,           // [out] character-to-glyph cluster map
            ushort*                     puCharProperties,       // [out] character properties
            int*                        pfCanGlyphAlone,        // [out] parallel to character codes: glyphing does not depend on neighbor?
            ref int                     glyphCount              // [out] glyph buffer length and returning actual glyph count
            )
        {
            Invariant.Assert(puGlyphsBuffer != null && piGlyphPropsBuffer != null);

            LsErr lserr = LsErr.None;
            LSRun lsrunFirst = null;

            try
            {
                LSRun[] lsruns = RemapLSRuns(plsplsruns, plsrunCount);
                lsrunFirst = lsruns[0];

                Debug.Assert(lsrunFirst.Shapeable != null);
                Debug.Assert(cchText > 0); // LineServices should not pass in zero character count;

                bool isRightToLeft = ((lsrunFirst.BidiLevel & 1) != 0);

                DWriteFontFeature[][] fontFeatures;
                uint[]                fontFeatureRanges;
                uint                  actualGlyphCount;
                checked 
                {
                    uint uCchText = (uint)cchText;
                    LSRun.CompileFeatureSet(lsruns, pcchPlsrun, uCchText, out fontFeatures, out fontFeatureRanges);
                    
                    GlyphTypeface glyphTypeface = lsrunFirst.Shapeable.GlyphTypeFace;
                    
                    FullText.Formatter.TextAnalyzer.GetGlyphs(
                        (ushort*)pwchText,
                        uCchText,
                        glyphTypeface.FontDWrite,
                        glyphTypeface.BlankGlyphIndex,
                        false,
                        isRightToLeft,
                        lsrunFirst.RunProp.CultureInfo,
                        fontFeatures,
                        fontFeatureRanges,
                        (uint)cgiGlyphBuffers,
                        FullText.TextFormattingMode,
                        lsrunFirst.Shapeable.ItemProps,
                        puClusterMap,
                        puCharProperties,
                        puGlyphsBuffer,
                        piGlyphPropsBuffer,
                        pfCanGlyphAlone,
                        out actualGlyphCount
                        );

                    glyphCount = (int)actualGlyphCount;
                   
                    if (glyphCount <= cgiGlyphBuffers)
                    {
                        fIsGlyphBuffersUsed = 1;
                    }
                    else
                    {
                        fIsGlyphBuffersUsed = 0;
                    }
                }
            }
            catch (Exception e)
            {
                SaveException(e, (Plsrun)(plsplsruns[0]), lsrunFirst);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetGlyphsRedefined", (Plsrun)(plsplsruns[0]), lsrunFirst);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
            
        }
        private Rect DrawTextDecoration(
            LSRun           lsrun,           // lsrun
            Brush           foregroundBrush, // default brush if text decoration has no pen
            LSPOINT         ptOrigin,        // drawing origin
            int             ulLength,        // underline length
            int             ulThickness,     // underline thickness
            LsTFlow         textFlow,        // text flow direction
            TextDecoration  textDecoration   //TextDecoration to be draw (add to sublinecollection
            )
        {
            switch (textFlow)
            {
                case LsTFlow.lstflowWS:
                case LsTFlow.lstflowNE:
                case LsTFlow.lstflowNW:

                    ptOrigin.x -= ulLength;
                    break;
            }

            TextMetrics.FullTextLine currentLine = Draw.CurrentLine;

            if (currentLine.RightToLeft)
            {
                ptOrigin.x = -ptOrigin.x;
            }

            int u = currentLine.LSLineUToParagraphU(ptOrigin.x);

            Point baselineOrigin = LSRun.UVToXY(
                Draw.LineOrigin,
                Draw.VectorToLineOrigin,
                u,
                currentLine.BaselineOffset,
                currentLine
                );

            Point lineOrigin = LSRun.UVToXY(
                Draw.LineOrigin,
                Draw.VectorToLineOrigin,
                u,
                ptOrigin.y + lsrun.BaselineMoveOffset,
                currentLine
                );



            double penThickness = 1.0;
            if (textDecoration.Pen != null)
            {
                penThickness = textDecoration.Pen.Thickness;
            }


            switch (textDecoration.PenThicknessUnit)
            {
                case TextDecorationUnit.FontRecommended:

                    penThickness = currentLine.Formatter.IdealToReal(ulThickness * penThickness);
                    break;

                case TextDecorationUnit.FontRenderingEmSize:
                    penThickness = currentLine.Formatter.IdealToReal(penThickness * lsrun.EmSize);
                    break;

                case TextDecorationUnit.Pixel:

                    break;

                default:
                    Debug.Assert(false, "Not supported TextDecorationUnit");
                    break;
            }


            penThickness = Math.Abs(penThickness);            




            double unitValue = 1.0;
            switch (textDecoration.PenOffsetUnit)
            {
                case TextDecorationUnit.FontRecommended:

                    unitValue = (lineOrigin.Y - baselineOrigin.Y);
                    break;

                case TextDecorationUnit.FontRenderingEmSize:
                    unitValue = currentLine.Formatter.IdealToReal(lsrun.EmSize);
                    break;

                case TextDecorationUnit.Pixel:
                    unitValue = 1.0;
                    break;

                default:
                    Debug.Assert(false, "Not supported TextDecorationUnit");
                    break;
            }


            double lineLength = currentLine.Formatter.IdealToReal(ulLength);

            DrawingContext drawingContext = Draw.DrawingContext;

            if (drawingContext != null)
            {      
                
                


                double drawingPenThickness = penThickness;



                Point  drawingLineOrigin   = lineOrigin;

                bool animated = !textDecoration.CanFreeze && (unitValue != 0);

                int pushCount = 0; // counter for the number of explicit DrawingContext.Push()
                

                Draw.SetGuidelineY(baselineOrigin.Y);                
                
                try 
                {
                    if (animated)
                    {                    









                        ScaleTransform scaleTransform = new ScaleTransform(
                            1.0,        // X scale
                            unitValue,  // y scale 
                            drawingLineOrigin.X,  // reference point of scaling
                            drawingLineOrigin.Y  // reference point of scaling
                            );
                           
                        TranslateTransform yTranslate = new TranslateTransform(
                            0,                       // x translate
                            textDecoration.PenOffset // y translate
                            );


                        drawingPenThickness = drawingPenThickness / Math.Abs(unitValue);
                                                    

                        drawingContext.PushTransform(scaleTransform);
                        pushCount++;
                        drawingContext.PushTransform(yTranslate);
                        pushCount++;

                    }
                    else
                    {

                        drawingLineOrigin.Y += unitValue * textDecoration.PenOffset;
                    }                    







                    drawingContext.PushGuidelineY2(baselineOrigin.Y, drawingLineOrigin.Y - drawingPenThickness * 0.5 - baselineOrigin.Y);
                    pushCount++;









                    if (textDecoration.Pen == null)
                    {

                        drawingContext.DrawRectangle(
                            foregroundBrush,               // fill using foreground
                            null,                          // null pen for rectangle stroke
                            new Rect(
                                drawingLineOrigin.X,
                                drawingLineOrigin.Y - drawingPenThickness * 0.5,
                                lineLength,
                                drawingPenThickness
                                )                    
                            );                    
                    }
                    else                    
                    {





                        Pen textDecorationPen = textDecoration.Pen.CloneCurrentValue();
                        if (Object.ReferenceEquals(textDecoration.Pen, textDecorationPen))
                        {

                            textDecorationPen = textDecoration.Pen.Clone();
                        }
                        
                        textDecorationPen.Thickness = drawingPenThickness;                  
                        

                        drawingContext.DrawLine(
                            textDecorationPen,
                            drawingLineOrigin,
                            new Point(drawingLineOrigin.X + lineLength, drawingLineOrigin.Y)
                            );
                    }
                }
                finally 
                {               
                    for (int i = 0; i < pushCount; i++)
                    {
                        drawingContext.Pop(); 
                    }

                    Draw.UnsetGuidelineY();
                }
            }
            
            return new Rect(
                lineOrigin.X,
                lineOrigin.Y + unitValue * textDecoration.PenOffset - penThickness * 0.5,
                lineLength,
                penThickness
                );
        }
        internal unsafe LsErr DrawTextRun(
            IntPtr          pols,               // Line Layout context
            Plsrun          plsrun,             // plsrun
            ref LSPOINT     ptText,             // [in] text origin
            char*           pwchText,           // character string
            int*            piCharAdvances,     // char advance widths
            int             cchText,            // text length
            LsTFlow         textFlow,           // text flow
            uint            displayMode,        // draw in transparent or opaque
            ref LSPOINT     ptRun,              // [in] run origin
            ref LsHeights   lsHeights,          // [in] run height
            int             dupRun,             // run length
            ref LSRECT      clipRect            // [in] from DisplayLine's clip rectangle param
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                GlyphRun glyphRun = ComputeUnshapedGlyphRun(
                    lsrun, 
                    textFlow, 
                    currentLine.Formatter,
                    true,       // origin of the glyph run provided at drawing time                    
                    ptText, 
                    dupRun, 
                    cchText, 
                    pwchText, 
                    piCharAdvances,
                    currentLine.IsJustified
                    );

                if (glyphRun != null)
                {
                    DrawingContext drawingContext = Draw.DrawingContext;

                    Draw.SetGuidelineY(glyphRun.BaselineOrigin.Y);                    

                    try 
                    {
                        _boundingBox.Union(
                            lsrun.DrawGlyphRun(
                                drawingContext, 
                                null,   // draw with the run's foreground brush
                                glyphRun
                                )
                            );
                    }
                    finally
                    {
                        Draw.UnsetGuidelineY();
                    }
                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawTextRun", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        private void DrawTextDecorationCollection(
            LSRun                     lsrun,
            uint                      locationMask,
            TextDecorationCollection  textDecorations,
            Brush                     foregroundBrush,
            int                       left,
            int                       underlineTop,
            int                       overlineTop,
            int                       strikethroughTop,
            int                       baselineTop,
            int                       length,
            int                       thickness,
            LsTFlow                   textFlow
            )
        {
            Invariant.Assert(textDecorations != null);

            foreach (TextDecoration td in textDecorations)
            {
                if (((1U << (int)td.Location) & locationMask) != 0)
                {
                    switch (td.Location)
                    {
                        case TextDecorationLocation.Underline:
                            _boundingBox.Union(
                                DrawTextDecoration(
                                    lsrun,
                                    foregroundBrush,
                                    new LSPOINT(left, underlineTop),
                                    length,
                                    thickness,
                                    textFlow,
                                    td
                                    )
                                );
                            break;

                        case TextDecorationLocation.OverLine:
                            _boundingBox.Union(
                                DrawTextDecoration(
                                    lsrun,
                                    foregroundBrush,
                                    new LSPOINT(left, overlineTop),
                                    length,
                                    thickness,
                                    textFlow,
                                    td
                                    )
                                );
                            break;

                        case TextDecorationLocation.Strikethrough:
                            _boundingBox.Union(
                                DrawTextDecoration(
                                    lsrun,
                                    foregroundBrush,
                                    new LSPOINT(left, strikethroughTop),
                                    length,
                                    thickness,
                                    textFlow,
                                    td
                                    )
                                );
                            break;

                        case TextDecorationLocation.Baseline:
                            _boundingBox.Union(
                                DrawTextDecoration(
                                    lsrun,
                                    foregroundBrush,
                                    new LSPOINT(left, baselineTop),
                                    length,
                                    thickness,
                                    textFlow,
                                    td
                                    )
                                );
                            break;
                    }
                }
            }
        }
        private void DrawTextDecorations(
            LSRun    lsrun,
            uint     locationMask,
            int      left,
            int      underlineTop,
            int      overlineTop,
            int      strikethroughTop,
            int      baselineTop,
            int      length,
            int      thickness,
            LsTFlow  textFlow
            )
        {
            TextMetrics.FullTextLine currentLine = Draw.CurrentLine;


            TextDecorationCollection textDecorations = currentLine.TextDecorations;
            if (textDecorations != null)
            {
                DrawTextDecorationCollection(
                    lsrun,
                    locationMask,
                    textDecorations,
                    currentLine.DefaultTextDecorationsBrush,
                    left,
                    underlineTop,
                    overlineTop,
                    strikethroughTop,
                    baselineTop,
                    length,
                    thickness,
                    textFlow
                    );
            }


            textDecorations = lsrun.RunProp.TextDecorations;
            if (textDecorations != null)
            {
                DrawTextDecorationCollection(
                    lsrun,
                    locationMask,
                    textDecorations,
                    lsrun.RunProp.ForegroundBrush,
                    left,
                    underlineTop,
                    overlineTop,
                    strikethroughTop,
                    baselineTop,
                    length,
                    thickness,
                    textFlow
                    );
            }
        }
        internal LsErr DrawUnderline(
            IntPtr pols,                // Line Layout context
            Plsrun      plsrun,         // plsrun
            uint        ulType,         // kind of underline
            ref LSPOINT ptOrigin,       // [in] drawing origin
            int         ulLength,       // underline length
            int         ulThickness,    // underline thickness
            LsTFlow     textFlow,       // text flow direction
            uint        displayMode,    // display mode
            ref LSRECT  clipRect        // [in] clipping rectangle
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;
            try
            {
                if (!TextStore.IsContent(plsrun))
                {

                    return LsErr.None;
                }

                lsrun = Draw.CurrentLine.GetRun(plsrun);

                const uint locationMask = (1U << (int)TextDecorationLocation.Underline);

                DrawTextDecorations(
                    lsrun,
                    locationMask,
                    ptOrigin.x,   // left
                    ptOrigin.y,   // underline top from LS
                    0,            // overline top; not used
                    0,            // strikethrough top; not used
                    0,            // baseline top; not used
                    ulLength,
                    ulThickness,
                    textFlow
                    );
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawUnderline", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        private unsafe GlyphRun ComputeUnshapedGlyphRun(
            LSRun               lsrun,              // LSrun used to shape the GlyphRun            
            LsTFlow             textFlow,           // flow direction
            TextFormatterImp    textFormatterImp,   // The TextFormatter Implementation
            bool                originProvided,     // flag indicate whether the origin of the run is provided                        
            LSPOINT             lsrunOrigin,        // physical start of the run
            int                 dupRun,             // width of the run
            int                 cchText,            // character count
            char                *pwchText,          // characters for display 
            int                 *piCharAdvances,    // character advance widths,
            bool                justify
            )
        {
            GlyphRun glyphRun = null;
            if (lsrun.Type == Plsrun.Text)
            {
                Debug.Assert(lsrun.Shapeable != null);
                Point runOrigin    = new Point();
                int nominalX = 0;
                int nominalY = 0;

                if (originProvided)
                {                   
                    TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                    
                    if (textFlow == LsTFlow.lstflowWS)
                    {
                        lsrunOrigin.x -= dupRun;
                    }

                    if (currentLine.RightToLeft)
                    {
                        lsrunOrigin.x = -lsrunOrigin.x;
                    }

                    if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify)
                    {
                        LSRun.UVToNominalXY(
                            Draw.LineOrigin,
                            Draw.VectorToLineOrigin,
                            currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                            lsrunOrigin.y + lsrun.BaselineMoveOffset,
                            currentLine,
                            out nominalX,
                            out nominalY
                            );
                    }
                    else
                    {
                        runOrigin = LSRun.UVToXY(
                            Draw.LineOrigin,
                            Draw.VectorToLineOrigin,
                            currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                            lsrunOrigin.y + lsrun.BaselineMoveOffset,
                            currentLine
                            );
                    }
                }



                
                char[] charString = new char[cchText];
                IList<double> charWidths;

                bool isRightToLeft = (lsrun.BidiLevel & 1) != 0;

                if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal)
                {
                    charWidths = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), cchText);
                    for (int i = 0; i < cchText; i++)
                    {
                        charString[i] = pwchText[i];
                        charWidths[i] = textFormatterImp.IdealToReal(piCharAdvances[i]);
                    }
                }
                else
                {
                    if (justify)
                    {
                        AdjustMetricsForDisplayModeJustifiedText(
                            pwchText,
                            piCharAdvances,
                            cchText,
                            isRightToLeft,
                            nominalX,
                            nominalY,
                            out runOrigin,
                            out charWidths
                            );
                    }
                    else
                    {
                        charWidths = new List<double>(cchText);
                        for (int i = 0; i < cchText; i++)
                        {
                            charWidths.Add(textFormatterImp.IdealToReal(piCharAdvances[i]));
                        }
                    }
                    for (int i = 0; i < cchText; i++)
                    {
                        charString[i] = pwchText[i];
                    }
                }

                

                glyphRun = lsrun.Shapeable.ComputeUnshapedGlyphRun(
                    runOrigin,
                    charString,
                    charWidths
                    );
            }            

            return glyphRun;
        } 
        internal unsafe LsErr GetGlyphPositions(
            IntPtr                      pols,               // Line Layout context
            IntPtr*                     plsplsruns,         // array of plsruns
            int*                        pcchPlsrun,         // array of character count per run
            int                         plsrunCount,        // number of runs
            LsDevice                    device,             // on reference or presentation device
            char*                       pwchText,           // character string
            ushort*                     puClusterMap,       // character-to-glyph cluster map
            ushort*                     puCharProperties,   // character properties
            int                         cchText,            // character count
            ushort*                     puGlyphs,           // glyph indices
            uint*                       piGlyphProperties,  // glyph properties
            int                         glyphCount,         // glyph count
            LsTFlow                     textFlow,           // text flow direction
            int*                        piGlyphAdvances,    // [out] glyph advances
            GlyphOffset*                piiGlyphOffsets     // [out] glyph offsets
            )
        {            
            LsErr lserr = LsErr.None;
            LSRun lsrunFirst = null;

            try
            {
                LSRun[] lsruns = RemapLSRuns(plsplsruns, plsrunCount);
                lsrunFirst = lsruns[0];

                bool isRightToLeft = ((lsrunFirst.BidiLevel & 1) != 0);

                GlyphOffset[] glyphOffset;

                GlyphTypeface glyphTypeface = lsrunFirst.Shapeable.GlyphTypeFace;

                DWriteFontFeature[][] fontFeatures;
                uint[] fontFeatureRanges;
                LSRun.CompileFeatureSet(lsruns, pcchPlsrun, checked((uint)cchText), out fontFeatures, out fontFeatureRanges);

                
                FullText.Formatter.TextAnalyzer.GetGlyphPlacements(
                    (ushort*)pwchText,
                    puClusterMap,
                    (ushort*)puCharProperties,
                    (uint)cchText,
                    puGlyphs,
                    piGlyphProperties,
                    (uint)glyphCount,
                    glyphTypeface.FontDWrite,
                    lsrunFirst.Shapeable.EmSize,
                    TextFormatterImp.ToIdeal,
                    false,
                    isRightToLeft,
                    lsrunFirst.RunProp.CultureInfo,
                    fontFeatures,
                    fontFeatureRanges,
                    FullText.TextFormattingMode,
                    lsrunFirst.Shapeable.ItemProps,
                    Util.PixelsPerDip,
                    piGlyphAdvances,
                    out glyphOffset
                    );

                for (int i = 0; i < glyphCount; ++i)
                {
                    piiGlyphOffsets[i].du = glyphOffset[i].du;
                    piiGlyphOffsets[i].dv = glyphOffset[i].dv;
                }                
                 
            }
            catch (Exception e)
            {
                SaveException(e, (Plsrun)(plsplsruns[0]), lsrunFirst);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetGlyphPositions", (Plsrun)(plsplsruns[0]), lsrunFirst);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
            
        }
        internal LsErr GetRunTextMetrics(
            System.IntPtr       pols,           // Line Layout context
            Plsrun              plsrun,         // plsrun
            LsDevice            lsDevice,       // kind of device
            LsTFlow             lstFlow,        // text flow
            ref LsTxM           lstTextMetrics  // [out] returning metrics
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                FullTextState fullText = FullText;
                TextStore store = fullText.StoreFrom(plsrun);
                lsrun = store.GetRun(plsrun);

                if (lsrun.Height > 0)
                {
                    lstTextMetrics.dvAscent = lsrun.BaselineOffset;
                    lstTextMetrics.dvMultiLineHeight = lsrun.Height;
                }
                else
                {
                    Typeface typeface = store.Pap.DefaultTypeface;
                    lstTextMetrics.dvAscent = (int)Math.Round(typeface.Baseline(store.Pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, fullText.TextFormattingMode));
                    lstTextMetrics.dvMultiLineHeight = (int)Math.Round(typeface.LineSpacing(store.Pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, fullText.TextFormattingMode));
                }

                lstTextMetrics.dvDescent = lstTextMetrics.dvMultiLineHeight - lstTextMetrics.dvAscent;
                lstTextMetrics.fMonospaced = 0;
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetRunTextMetrics", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal unsafe LsErr DrawGlyphs(
            IntPtr                      pols,                       // Line Layout context
            Plsrun                      plsrun,                     // plsrun
            char*                       pwchText,                   // character string
            ushort*                     puClusterMap,               // character-to-cluster map
            ushort*                     puCharProperties,           // character properties
            int                         charCount,                  // character count
            ushort*                     puGlyphs,                   // glyph indices
            int*                        piJustifiedGlyphAdvances,   // justified glyph advances
            int*                        piGlyphAdvances,            // original ideal glyph advances
            GlyphOffset*                piiGlyphOffsets,            // glyph offsets
            uint*                       piGlyphProperties,          // glyph properties
            LsExpType*                  plsExpType,                 // glyph expansion types
            int                         glyphCount,                 // glyph count
            LsTFlow                     textFlow,                   // text flow
            uint                        displayMode,                // draw transparent or opaque
            ref LSPOINT                 ptRun,                      // [in] display position (at baseline)
            ref LsHeights               lsHeights,                  // [in] run height metrics
            int                         runWidth,                   // run overall advance width
            ref LSRECT                  clippingRect                // [in] clipping rectangle if any applied
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                Debug.Assert(TextStore.IsContent(plsrun) && lsrun.Shapeable != null);

                GlyphRun glyphRun = ComputeShapedGlyphRun(
                    lsrun,
                    currentLine.Formatter,
                    true,           // origin of the glyph run provided at drawing time
                    ptRun,
                    charCount,
                    pwchText,
                    puClusterMap,
                    glyphCount,
                    puGlyphs,
                    piJustifiedGlyphAdvances,
                    piiGlyphOffsets,
                    currentLine.IsJustified
                    );

                if (glyphRun != null)
                {
                    DrawingContext drawingContext = Draw.DrawingContext;

                    Draw.SetGuidelineY(glyphRun.BaselineOrigin.Y);

                    try 
                    {
                        _boundingBox.Union(
                            lsrun.DrawGlyphRun(
                                drawingContext, 
                                null,     // draw with the run's foreground
                                glyphRun
                                )
                            );
                    }
                    finally 
                    {
                        Draw.UnsetGuidelineY();
                    }

                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawGlyphs", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal LsErr GetDurMaxExpandRagged(
            IntPtr      pols,               // Line Layout context
            Plsrun      plsrun,             // plsrun
            LsTFlow     lstFlow,            // text flow
            ref int     maxExpandRagged     // [out] em width
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {



                lsrun = FullText.StoreFrom(plsrun).GetRun(plsrun);
                maxExpandRagged = lsrun.EmSize;
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetDurMaxExpandRagged", 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;
        }
        internal LsErr GetRunStrikethroughInfo(
            IntPtr          pols,           // Line Layout context
            Plsrun          plsrun,         // plsrun
            ref LsHeights   lsHeights,      // run height
            LsTFlow         textFlow,       // text flow direction
            ref LsStInfo    stInfo          // [out] result strikethrough info
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                lsrun = Draw.CurrentLine.GetRun(plsrun);

                Debug.Assert(
                    !TextStore.IsContent(plsrun) || lsrun.Type == Plsrun.Text || lsrun.Type == Plsrun.InlineObject,
                    "Invalid run"
                    );

                stInfo = new LsStInfo();

                double strikeThroughPositionInEm;
                double strikeThroughThicknessInEm;

                GetLSRunStrikethroughMetrics(lsrun, out strikeThroughPositionInEm, out strikeThroughThicknessInEm);

                stInfo.cNumberOfLines = 1;
                stInfo.dvpLowerStrikethroughOffset = (int)Math.Round(lsrun.EmSize * strikeThroughPositionInEm);
                stInfo.dvpLowerStrikethroughSize = (int)Math.Round(lsrun.EmSize * strikeThroughThicknessInEm);

                Debug.Assert(stInfo.dvpLowerStrikethroughSize >= 0);





                if (stInfo.dvpLowerStrikethroughSize <= 0)
                    stInfo.dvpLowerStrikethroughSize = 1;
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetRunStrikethroughInfo", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        /// <summary>
        /// From LsTFlow to FlowDirection
        /// </summary>
        public static FlowDirection LsTFlowToFlowDirection(LsTFlow lstflow)
        {
            switch (lstflow)
            {
                //case LsTFlow.lstflowDefault:
                case LsTFlow.lstflowES:
                case LsTFlow.lstflowEN:
                    return FlowDirection.LeftToRight;


                case LsTFlow.lstflowWS:
                case LsTFlow.lstflowWN:
                    return FlowDirection.RightToLeft;

                // vertical flow is not supported
                case LsTFlow.lstflowSE:
                case LsTFlow.lstflowSW:
                case LsTFlow.lstflowNE:
                case LsTFlow.lstflowNW:
                    break;
            }
            return FlowDirection.LeftToRight;
        }