/// <summary>
            /// Get character hit from specified hittest distance relative to line start
            /// </summary>
            private CharacterHit CharacterHitFromDistance(int hitTestDistance)
            {
                // assuming the first cp of the line
                CharacterHit characterHit = new CharacterHit(_cpFirst, 0);

                if(_ploline.Value == IntPtr.Zero)
                {
                    // Returning the first cp for the empty line
                    return characterHit;
                }

                if (    HasCollapsed
                    &&  _collapsedRange != null
                    &&  _collapsingSymbol != null
                    )
                {
                    int lineEndDistance = _metrics._textStart + _metrics._textWidthAtTrailing;
                    int rangeWidth = TextFormatterImp.RealToIdeal(_collapsingSymbol.Width);

                    if (hitTestDistance >= lineEndDistance - rangeWidth)
                    {
                        if (lineEndDistance - hitTestDistance < rangeWidth / 2)
                        {
                            // The hit-test distance is within the trailing edge of the collapsed range,
                            // return the character hit at the beginning of the range.
                            return new CharacterHit(_collapsedRange.TextSourceCharacterIndex, _collapsedRange.Length);
                        }

                        // The hit-test distance is within the leading edge of the collapsed range,
                        // return the character hit at the beginning of the range.
                        return new CharacterHit(_collapsedRange.TextSourceCharacterIndex, 0);
                    }
                }

                LsTextCell lsTextCell;
                LsQSubInfo[] sublineInfo = new LsQSubInfo[_depthQueryMax];
                int actualSublineCount;

                QueryLinePointPcp(
                    new Point(hitTestDistance, 0),
                    sublineInfo,
                    out actualSublineCount,
                    out lsTextCell
                    );

                if (actualSublineCount > 0 && lsTextCell.dupCell > 0)
                {
                    // the last subline contains the run that owns the querying lscp
                    LSRun lsrun = GetRun((Plsrun)sublineInfo[actualSublineCount - 1].plsrun);

                    // Assuming caret stops at every codepoint.
                    //
                    // LsTextCell.lscpEndCell is the index to the last lscp still in the cell.
                    // The number of LSCP within the text cell is equal to the number of CP.
                    int caretStopCount = lsTextCell.lscpEndCell + 1 - lsTextCell.lscpStartCell;

                    int codepointsToNextCaretStop = lsrun.IsHitTestable ? 1 : lsrun.Length;

                    if (    lsrun.IsHitTestable
                        &&  (   lsrun.HasExtendedCharacter
                            ||  lsrun.NeedsCaretInfo)
                        )
                    {
                        // A hit-testable run with caret stops at every cluster boundaries,
                        // e.g. run with combining mark, with extended characters or complex scripts such as Thai
                        codepointsToNextCaretStop = caretStopCount;
                        caretStopCount = 1;
                    }

                    // All the UV coordinate in subline are in main direction. If the last subline where
                    // we hittest runs in the opposite direction, the logical advance from text cell start cp
                    // will be negative value.
                    int direction = (sublineInfo[actualSublineCount - 1].lstflowSubLine == sublineInfo[0].lstflowSubLine) ? 1 : -1;
                    hitTestDistance = (hitTestDistance - lsTextCell.pointUvStartCell.x) * direction;

                    Invariant.Assert(caretStopCount > 0);
                    int wholeAdvance = lsTextCell.dupCell / caretStopCount;
                    int remainingAdvance = lsTextCell.dupCell % caretStopCount;

                    for (int i = 0; i < caretStopCount; i++)
                    {
                        int caretAdvance = wholeAdvance;
                        if (remainingAdvance > 0)
                        {
                            caretAdvance++;
                            remainingAdvance--;
                        }

                        if (hitTestDistance <= caretAdvance)
                        {
                            if (hitTestDistance > caretAdvance / 2)
                            {
                                // hittest at the trailing edge of the current caret stop
                                return new CharacterHit(GetExternalCp(lsTextCell.lscpStartCell) + i, codepointsToNextCaretStop);
                            }

                            // hittest at the leading edge of the current caret stop
                            return new CharacterHit(GetExternalCp(lsTextCell.lscpStartCell) + i, 0);
                        }

                        hitTestDistance -= caretAdvance;
                    }

                    // hittest beyond the last caret stop, return the trailing edge of the last caret stop
                    return new CharacterHit(GetExternalCp(lsTextCell.lscpStartCell) + caretStopCount - 1, codepointsToNextCaretStop);
                }

                return characterHit;
            }
 /// <summary>
 /// Return the ending edge of the run relative to its own flow direction.
 /// </summary>
 private int GetEndOfRunDistance(
     LsQSubInfo[] sublines,
     int          index
     )
 {
     return sublines[index].pointUvStartRun.x +
         ( sublines[index].lstflowSubLine == sublines[0].lstflowSubLine ?
               sublines[index].dupRun
             : -sublines[index].dupRun
         );
 }
            private void QueryLineCpPpoint(
                int                 lscpQuery,
                LsQSubInfo[]        subLineInfo,
                out int             actualDepthQuery,
                out LsTextCell      lsTextCell
                )
            {
                Debug.Assert(_ploline.Value != IntPtr.Zero);

                LsErr lserr = LsErr.None;

                lsTextCell = new LsTextCell();

                // Never hit LS with any LSCP beyond its last, the result is unreliable and varies between drops.
                int lscpValidQuery = (lscpQuery < _metrics._lscpLim ? lscpQuery : _metrics._lscpLim - 1);

                unsafe
                {
                    fixed(LsQSubInfo* plsqsubl = subLineInfo)
                    {
                        lserr = UnsafeNativeMethods.LoQueryLineCpPpoint(
                            _ploline.Value,
                            lscpValidQuery,
                            subLineInfo.Length,
                            (System.IntPtr)plsqsubl,
                            out actualDepthQuery,
                            out lsTextCell
                            );
                    }
                }

                if(lserr != LsErr.None)
                {
                    TextFormatterContext.ThrowExceptionFromLsError(SR.Get(SRID.QueryLineFailure, lserr), lserr);
                }

                if (lsTextCell.lscpEndCell < lsTextCell.lscpStartCell)
                {
                    // When hit-testing is done on a generated hyphen of a hyphenated word, LS can only tell
                    // the start LSCP and not the end LSCP. Argurably this is LS bug. In such situation they
                    // should assume the end LSCP being the last LSCP of the line.
                    //
                    // However our code assumes that LS must tell both and the text cell must have size greater
                    // than one codepoint. We count on that to reliably advance the caret position.
                    //
                    // The LSPTS bug#1005 has been filed and while we are still debating, we need to unblock
                    // ourselves. What we can do is to assume that the next caret stop in this case is always
                    // the next codepoint.
                    lsTextCell.lscpEndCell = lsTextCell.lscpStartCell;
                }
            }
            /// <summary>
            /// Base level is the highest subline's level that both sets of sublines have in common.
            /// This method starts collecting text bounds at the specified LSCP. The first bounds being collected
            /// is the one from the LSCP to the start of run at the base level subline. The subsequent bounds are
            /// from the start of the higher level subline to the start of the run within the same subline, until
            /// it reaches the immediate enclosing subline of the last LSCP.
            /// </summary>
            private void CollectTextBoundsFromBaseLevel(
                ArrayList    boundsList,
                ref int      lscpCurrent,
                ref int      currentDistance,
                LsQSubInfo[] sublines,
                int          sublineDepth,
                int          baseLevelDepth
                )
            {
                // lscpCurrent is after the run end of the 1st cp. It must not be in the run of the last cp
                // because the two runs don't overlap at above base level.
                Invariant.Assert(lscpCurrent <= sublines[baseLevelDepth - 1].lscpFirstRun);

                // Collect the text bounds from the LSCP to the start of run at the base level subline.
                AddValidTextBounds(
                    boundsList,
                    new TextBounds(
                        LSRun.RectUV(
                            new Point(0, 0),
                            new LSPOINT(LSLineUToParagraphU(currentDistance), 0),
                            new LSPOINT(
                                LSLineUToParagraphU(sublines[baseLevelDepth - 1].pointUvStartRun.x),
                                _metrics._height
                            ),
                            this
                        ),
                        Convert.LsTFlowToFlowDirection(sublines[baseLevelDepth - 1].lstflowSubLine),
                        CalculateTextRunBounds(lscpCurrent, sublines[baseLevelDepth - 1].lscpFirstRun)
                     )
                );

                // Collect text bounds from start of subline to start of run at higher level sublines until it
                // reaches the immediate enclosing subline of the last LSCP.
                for (int i = baseLevelDepth; i < sublineDepth - 1; i++)
                {
                    AddValidTextBounds(
                        boundsList,
                        new TextBounds(
                            LSRun.RectUV(
                                new Point(0, 0),
                                new LSPOINT(LSLineUToParagraphU(sublines[i].pointUvStartSubLine.x), 0),
                                new LSPOINT(
                                    LSLineUToParagraphU(sublines[i].pointUvStartRun.x),
                                    _metrics._height
                                ),
                                this
                            ),
                            Convert.LsTFlowToFlowDirection(sublines[i].lstflowSubLine),
                            CalculateTextRunBounds(
                                sublines[i].lscpFirstSubLine,
                                sublines[i].lscpFirstRun
                            )
                        )
                    );
                };

                // Move the current LSCP and distance to the start of the immediate enclosing subline.
                lscpCurrent = sublines[sublineDepth - 1].lscpFirstSubLine;
                currentDistance = sublines[sublineDepth - 1].pointUvStartSubLine.x;
            }
 /// <summary>
 /// Return the ending edge of the subline relative to its own flow direction
 /// </summary>
 private int GetEndOfSublineDistance(
     LsQSubInfo[] sublines,
     int          index
     )
 {
     return sublines[index].pointUvStartSubLine.x +
         ( sublines[index].lstflowSubLine == sublines[0].lstflowSubLine ?
               sublines[index].dupSubLine
             : -sublines[index].dupSubLine
         );
 }
            /// <summary>
            /// Base level is the highest subline's level that both sets of sublines have in common.
            /// This method starts collecting text bounds at the specified LSCP. The first bounds being
            /// collected is the one from the LSCP to the end of its immediate enclosing subline. The
            /// subsequent bounds are from the end of run to the end of subline of the lower level until
            /// it reaches the base level.
            /// </summary>
            private void CollectTextBoundsToBaseLevel(
                ArrayList    boundsList,
                ref int      lscpCurrent,
                ref int      currentDistance,
                LsQSubInfo[] sublines,
                int          sublineDepth,
                int          lscpEnd,
                out int      baseLevelDepth
                )
            {
                baseLevelDepth = sublineDepth;

                if (lscpEnd < sublines[sublineDepth - 1].lscpFirstSubLine + sublines[sublineDepth - 1].lsdcpSubLine)
                {
                    // The immedidate enclosing subline already contains the lscp end. It means we are already
                    // at base level.
                    return;
                }

                // Collect text bounds from the current lscp to the end of the immediate enclosing subline.
                AddValidTextBounds(
                    boundsList,
                    new TextBounds(
                        LSRun.RectUV(
                            new Point(0, 0),
                            new LSPOINT(LSLineUToParagraphU(currentDistance), 0),
                            new LSPOINT(
                                LSLineUToParagraphU(GetEndOfSublineDistance(sublines, sublineDepth - 1)),
                                _metrics._height
                            ),
                            this
                        ),
                        Convert.LsTFlowToFlowDirection(sublines[sublineDepth - 1].lstflowSubLine),
                        CalculateTextRunBounds(lscpCurrent, sublines[sublineDepth - 1].lscpFirstSubLine + sublines[sublineDepth - 1].lsdcpSubLine)
                    )
                );

                // Collect text bounds from end of run to the end of subline at lower levels until we reach the
                // common level. We reach common level when the subline at that level contains the lscpEnd.
                for (   baseLevelDepth = sublineDepth - 1;
                        baseLevelDepth > 0 && (lscpEnd >= sublines[baseLevelDepth - 1].lscpFirstSubLine + sublines[baseLevelDepth - 1].lsdcpSubLine);
                        baseLevelDepth--
                    )
                {
                    int sublineIndex = baseLevelDepth - 1;
                    AddValidTextBounds(
                        boundsList,
                        new TextBounds(
                            LSRun.RectUV(
                                new Point(0, 0),
                                new LSPOINT(
                                    LSLineUToParagraphU(GetEndOfRunDistance(sublines, sublineIndex)),
                                    0
                                ),
                                new LSPOINT(
                                    LSLineUToParagraphU(GetEndOfSublineDistance(sublines, sublineIndex)),
                                    _metrics._height
                                ),
                                this
                            ),
                            Convert.LsTFlowToFlowDirection(sublines[sublineIndex].lstflowSubLine),
                            CalculateTextRunBounds(
                                sublines[sublineIndex].lscpFirstRun + sublines[sublineIndex].lsdcpRun,
                                sublines[sublineIndex].lscpFirstSubLine + sublines[sublineIndex].lsdcpSubLine
                            )
                        )
                    );
                }

                // base level depth must be at least 1 because both cp at least share the main line.
                Invariant.Assert(baseLevelDepth >= 1);

                // Move the current LSCP and distance to the end of run on the base level subline.
                lscpCurrent = sublines[baseLevelDepth - 1].lscpFirstRun + sublines[baseLevelDepth - 1].lsdcpRun;
                currentDistance = GetEndOfRunDistance(sublines, baseLevelDepth - 1);
            }
            /// <summary>
            /// Client to get an array of bounding rectangles of a range of characters within a text line.
            /// </summary>
            /// <param name="firstTextSourceCharacterIndex">index of first character of specified range</param>
            /// <param name="textLength">number of characters of the specified range</param>
            /// <returns>an array of bounding rectangles.</returns>
            public override IList<TextBounds> GetTextBounds(
                int     firstTextSourceCharacterIndex,
                int     textLength
                )
            {
                if ((_statusFlags & StatusFlags.IsDisposed) != 0)
                {
                    throw new ObjectDisposedException(SR.Get(SRID.TextLineHasBeenDisposed));
                }

                if(textLength == 0)
                {
                    throw new ArgumentOutOfRangeException("textLength", SR.Get(SRID.ParameterMustBeGreaterThanZero));
                }

                if(textLength < 0)
                {
                    firstTextSourceCharacterIndex += textLength;
                    textLength = -textLength;
                }

                if(firstTextSourceCharacterIndex < _cpFirst)
                {
                    textLength += (firstTextSourceCharacterIndex - _cpFirst);
                    firstTextSourceCharacterIndex = _cpFirst;
                }

                if(firstTextSourceCharacterIndex > _cpFirst + _metrics._cchLength - textLength)
                {
                    textLength = (_cpFirst + _metrics._cchLength - firstTextSourceCharacterIndex);
                }

                if (_ploline.Value == IntPtr.Zero)
                {
                    return CreateDegenerateBounds();
                }

                Point position = new Point(0,0);


                // get first cp sublines & text cell

                int firstDepth;
                LsTextCell firstTextCell;
                LsQSubInfo[] firstSublines = new LsQSubInfo[_depthQueryMax];

                int lscpFirst = GetInternalCp(firstTextSourceCharacterIndex);

                QueryLineCpPpoint(
                    lscpFirst,
                    firstSublines,
                    out firstDepth,
                    out firstTextCell
                    );

                if(firstDepth <= 0)
                {
                    // this happens for empty line (line containing only EOP)
                    return CreateDegenerateBounds();
                }


                // get last cp sublines & text cell

                int lastDepth;
                LsTextCell lastTextCell;
                LsQSubInfo[] lastSublines = new LsQSubInfo[_depthQueryMax];

                int lscpEnd = GetInternalCp(firstTextSourceCharacterIndex + textLength - 1);

                QueryLineCpPpoint(
                    lscpEnd,
                    lastSublines,
                    out lastDepth,
                    out lastTextCell
                    );

                if(lastDepth <= 0)
                {
                    // This should never happen but if it does, we still cant throw here.
                    // We must return something even though it's a degenerate bounds or
                    // client hittesting code will just crash.
                    Debug.Assert(false);
                    return CreateDegenerateBounds();
                }

                // check if collapsing symbol is wholely selected
                bool collapsingSymbolSelected =
                    (   _collapsingSymbol != null
                    &&  _collapsedRange != null
                    &&  firstTextSourceCharacterIndex < _collapsedRange.TextSourceCharacterIndex
                    &&  firstTextSourceCharacterIndex + textLength - _collapsedRange.TextSourceCharacterIndex > _collapsedRange.Length / 2
                    );

                TextBounds[] bounds = null;
                ArrayList boundsList = null;

                // By default, if the hittested CP is visible, then we want cpFirst to hit
                // on the leading edge of the first visible cp, and cpEnd to hit on the trailing edge of the
                // last visible cp.
                bool isCpFirstTrailing = false;
                bool isCpEndTrailing = true;

                if (lscpFirst > firstTextCell.lscpEndCell)
                {
                   // when cpFirst is after the last visible cp, then it hits the trailing edge of that cp
                   isCpFirstTrailing = true;
                }

                if (lscpEnd < lastTextCell.lscpStartCell)
                {
                   // when cpEnd is before the first visible cp, then it hits the leading edge of that cp
                   isCpEndTrailing = false;
                }

                if (firstDepth == lastDepth && firstSublines[firstDepth - 1].lscpFirstSubLine == lastSublines[lastDepth - 1].lscpFirstSubLine)
                {
                    // first and last cp are within the same subline

                    int count = collapsingSymbolSelected ? 2 : 1;

                    bounds = new TextBounds[count];
                    bounds[0] =
                        new TextBounds(
                            LSRun.RectUV(
                                position,
                                new LSPOINT(
                                    LSLineUToParagraphU(
                                        GetDistanceInsideTextCell(
                                            lscpFirst,
                                            isCpFirstTrailing,
                                            firstSublines,
                                            firstDepth,
                                            ref firstTextCell
                                            ) + firstTextCell.pointUvStartCell.x
                                        ),
                                    0
                                    ),
                                new LSPOINT(
                                    LSLineUToParagraphU(
                                        GetDistanceInsideTextCell(
                                            lscpEnd,
                                            isCpEndTrailing,
                                            lastSublines,
                                            lastDepth,
                                            ref lastTextCell
                                            ) + lastTextCell.pointUvStartCell.x
                                        ),
                                    _metrics._height
                                    ),
                                this
                                ),
                            Convert.LsTFlowToFlowDirection(firstSublines[firstDepth - 1].lstflowSubLine),
                            CalculateTextRunBounds(lscpFirst, lscpEnd + 1)
                            );

                    if (count > 1)
                    {
                        bounds[1] = CreateCollapsingSymbolBounds();
                    }
                }
                else
                {
                    // first and last cp are not in the same subline.
                    boundsList = new ArrayList(2);

                    int lscpCurrent = lscpFirst;

                    // The hittested cp can be outside of the returned sublines when it is a hidden cp.
                    // We should not pass beyond the end of the returned sublines.
                    int lscpEndInSubline = Math.Min(
                        lscpEnd,
                        lastSublines[lastDepth - 1].lscpFirstSubLine + lastSublines[lastDepth - 1].lsdcpSubLine - 1
                        );

                    int currentDistance = GetDistanceInsideTextCell(
                        lscpFirst,
                        isCpFirstTrailing,
                        firstSublines,
                        firstDepth,
                        ref firstTextCell
                    ) + firstTextCell.pointUvStartCell.x;

                    int baseLevelDepth;

                    CollectTextBoundsToBaseLevel(
                        boundsList,
                        ref lscpCurrent,
                        ref currentDistance,
                        firstSublines,
                        firstDepth,
                        lscpEndInSubline,
                        out baseLevelDepth
                    );

                    if (baseLevelDepth < lastDepth)
                    {
                        CollectTextBoundsFromBaseLevel(
                            boundsList,
                            ref lscpCurrent,
                            ref currentDistance,
                            lastSublines,
                            lastDepth,
                            baseLevelDepth
                        );
                    }

                    // Collect the bounds from the start of the immediate enclosing subline of the last LSCP
                    // to the hittested text cell.
                    AddValidTextBounds(
                        boundsList,
                        new TextBounds(
                            LSRun.RectUV(
                                position,
                                new LSPOINT(
                                    LSLineUToParagraphU(currentDistance),
                                    0
                                ),
                                new LSPOINT(
                                    LSLineUToParagraphU(
                                        GetDistanceInsideTextCell(
                                            lscpEnd,
                                            isCpEndTrailing,
                                            lastSublines,
                                            lastDepth,
                                            ref lastTextCell
                                            ) + lastTextCell.pointUvStartCell.x
                                        ),
                                    _metrics._height
                                ),
                                this
                            ),
                            Convert.LsTFlowToFlowDirection(lastSublines[lastDepth - 1].lstflowSubLine),
                            CalculateTextRunBounds(lscpCurrent, lscpEnd + 1)
                        )
                    );
                }

                if (bounds == null)
                {
                    Debug.Assert(boundsList != null);
                    if (boundsList.Count > 0)
                    {
                        if (collapsingSymbolSelected)
                        {
                            // add one more for collapsed symbol
                            AddValidTextBounds(boundsList, CreateCollapsingSymbolBounds());
                        }

                        bounds = new TextBounds[boundsList.Count];
                        for (int i = 0; i < boundsList.Count; i++)
                        {
                            bounds[i] = (TextBounds)boundsList[i];
                        }
                    }
                    else
                    {
                        // No non-zerowidth bounds detected, fallback to the position of first cp
                        // This can happen if hidden run is hittest'd.

                        int u =  LSLineUToParagraphU(
                            GetDistanceInsideTextCell(
                                lscpFirst,
                                isCpFirstTrailing,
                                firstSublines,
                                firstDepth,
                                ref firstTextCell
                                ) + firstTextCell.pointUvStartCell.x
                            );

                        bounds = new TextBounds[]
                        {
                            new TextBounds(
                                LSRun.RectUV(
                                    position,
                                    new LSPOINT(u, 0),
                                    new LSPOINT(u, _metrics._height),
                                    this
                                    ),
                                Convert.LsTFlowToFlowDirection(firstSublines[firstDepth - 1].lstflowSubLine),
                                null
                                )
                        };
                    }
                }

                return bounds;
            }
            /// <summary>
            /// Given a specified current character index, calculate the character index
            /// to the closest caret stop before or at the current index; and the number
            /// of codepoints from the closest caret stop to the next caret stop.
            /// </summary>
            private bool GetNextOrPreviousCaretStop(
                int                      currentIndex,
                CaretDirection           direction,
                out int                  caretStopIndex,
                out int                  offsetToNextCaretStopIndex
                )
            {
                caretStopIndex = currentIndex;
                offsetToNextCaretStopIndex = 0;

                if (    HasCollapsed
                    &&  _collapsedRange != null
                    &&  currentIndex >= _collapsedRange.TextSourceCharacterIndex
                    )
                {
                    // current index is within collapsed range,
                    caretStopIndex = _collapsedRange.TextSourceCharacterIndex;

                    if (currentIndex < _collapsedRange.TextSourceCharacterIndex + _collapsedRange.Length)
                        offsetToNextCaretStopIndex = _collapsedRange.Length;

                    return true;
                }

                LsQSubInfo[] sublineInfo = new LsQSubInfo[_depthQueryMax];
                LsTextCell lsTextCell = new LsTextCell();

                int lscpVisisble = GetInternalCp(currentIndex);
                bool found = FindNextOrPreviousVisibleCp(lscpVisisble, direction, out lscpVisisble);

                if (!found)
                {
                    return false; // there is no caret stop anymore in the given direction.
                }

                int actualSublineCount;

                QueryLineCpPpoint(
                    lscpVisisble,
                    sublineInfo,
                    out actualSublineCount,
                    out lsTextCell
                    );

                // Locate the current caret stop
                caretStopIndex = GetExternalCp(lsTextCell.lscpStartCell);

                if (    actualSublineCount > 0
                    &&  lscpVisisble >= lsTextCell.lscpStartCell
                    &&  lscpVisisble <= lsTextCell.lscpEndCell
                    )
                {
                    // the last subline contains the run that owns the querying lscp
                    LSRun lsrun = GetRun((Plsrun)sublineInfo[actualSublineCount - 1].plsrun);

                    if (lsrun.IsHitTestable)
                    {
                        if (    lsrun.HasExtendedCharacter
                            ||  (direction != CaretDirection.Backspace && lsrun.NeedsCaretInfo)
                            )
                        {
                            // LsTextCell.lscpEndCell is the index to the last lscp still in the cell.
                            // The number of LSCP within the text cell is equal to the number of CP.
                            offsetToNextCaretStopIndex = lsTextCell.lscpEndCell + 1 - lsTextCell.lscpStartCell;
                        }
                        else
                        {
                            // caret stops before every codepoint
                            caretStopIndex = GetExternalCp(lscpVisisble);
                            offsetToNextCaretStopIndex = 1;
                        }
                    }
                    else
                    {
                        // run is not hit-testable, caret navigation is not allowed in the run,
                        // the next caret stop is therefore either at the end of the run or the end of the line whichever reached first.
                        offsetToNextCaretStopIndex = Math.Min(Length, lsrun.Length - caretStopIndex + lsrun.OffsetToFirstCp + _cpFirst);
                    }
                }

                return true;
            }
            /// <summary>
            /// Get distance from the start of text cell to the specified lscp
            /// </summary>
            private int GetDistanceInsideTextCell(
                int                 lscpCurrent,
                bool                isTrailing,
                LsQSubInfo[]        sublineInfo,
                int                 actualSublineCount,
                ref LsTextCell      lsTextCell
                )
            {
                int distanceInCell = 0;

                // All the UV coordinate in subline are in main direction. If the last subline where
                // we hittest runs in the opposite direction, the logical advance from text cell start cp
                // will be negative value.
                int direction = (sublineInfo[actualSublineCount - 1].lstflowSubLine == sublineInfo[0].lstflowSubLine) ? 1 : -1;

                // LsTextCell.lscpEndCell is the index to the last lscp still in the cell.
                // The number of LSCP within the text cell is equal to the number of CP.
                //
                // Assuming caret stops at every codepoint in the run.
                int caretStopCount = lsTextCell.lscpEndCell + 1 - lsTextCell.lscpStartCell;
                int codepointsFromStartCell = lscpCurrent - lsTextCell.lscpStartCell;

                // the last subline contains the run that owns the querying lscp
                LSRun lsrun = GetRun((Plsrun)sublineInfo[actualSublineCount - 1].plsrun);

                if (    lsrun.IsHitTestable
                    &&  (   lsrun.HasExtendedCharacter
                        ||  lsrun.NeedsCaretInfo)
                    )
                {
                    // A hit-testable run with caret stops at every cluster boundaries,
                    // e.g. run with combining mark, with extended characters or complex scripts such as Thai
                    caretStopCount = 1;
                }

                Invariant.Assert(caretStopCount > 0);
                int wholeAdvance = lsTextCell.dupCell / caretStopCount;
                int remainingAdvance = lsTextCell.dupCell % caretStopCount;

                for (int i = 1; i <= caretStopCount; i++)
                {
                    int caretAdvance = wholeAdvance;
                    if (remainingAdvance > 0)
                    {
                        caretAdvance++;
                        remainingAdvance--;
                    }

                    if (codepointsFromStartCell < i)
                    {
                        if (isTrailing)
                        {
                            // hit-test at the trailing edge of the current caret stop, include the current caret advance
                            return (distanceInCell + caretAdvance) * direction;
                        }

                        // hit-test at the leading edge of the current caret stop, return the accumulated distance
                        return distanceInCell * direction;
                    }

                    distanceInCell += caretAdvance;
                }

                // hit-test beyond the last caret stop, return the total accumated distance up to the trailing edge of the last caret stop.
                return distanceInCell * direction;
            }
            /// <summary>
            /// Get hittest distance relative to line start from specified character hit
            /// </summary>
            private int DistanceFromCharacterHit(CharacterHit characterHit)
            {
                int hitTestDistance = 0;

                if (_ploline.Value == IntPtr.Zero)
                {
                    // Returning start of the line for empty line
                    return hitTestDistance;
                }

                if (characterHit.FirstCharacterIndex >= _cpFirst + _metrics._cchLength)
                {
                    // Returning line width for character hit beyond the last caret stop
                    return _metrics._textStart + _metrics._textWidthAtTrailing;
                }

                if (    HasCollapsed
                    &&  _collapsedRange != null
                    &&  characterHit.FirstCharacterIndex >= _collapsedRange.TextSourceCharacterIndex
                    )
                {
                    // The current character hit is beyond the beginning of the collapsed range
                    int lineEndDistance = _metrics._textStart + _metrics._textWidthAtTrailing;

                    if (    characterHit.FirstCharacterIndex >= _collapsedRange.TextSourceCharacterIndex + _collapsedRange.Length
                        ||  characterHit.TrailingLength != 0
                        || _collapsingSymbol == null
                        )
                    {
                        // The current character hit either hits outside,
                        // or it's at the trailing edge of the collapsed range
                        return lineEndDistance;
                    }

                    return lineEndDistance - TextFormatterImp.RealToIdeal(_collapsingSymbol.Width);
                }

                int actualSublineCount;
                LsTextCell lsTextCell;
                LsQSubInfo[] sublineInfo = new LsQSubInfo[_depthQueryMax];

                int lscpCurrent = GetInternalCp(characterHit.FirstCharacterIndex);

                QueryLineCpPpoint(
                    lscpCurrent,
                    sublineInfo,
                    out actualSublineCount,
                    out lsTextCell
                    );

                if (actualSublineCount > 0)
                {
                    return lsTextCell.pointUvStartCell.x + GetDistanceInsideTextCell(
                        lscpCurrent,
                        characterHit.TrailingLength != 0,
                        sublineInfo,
                        actualSublineCount,
                        ref lsTextCell
                        );
                }

                return hitTestDistance;
            }