예제 #1
0
        public virtual SizeF_ SizeOfPosition(Graphics dc, int pos)
        {
            if (pos >= line.TextLengthWithoutEnding() && line.document.multiline)
            {
                return(SizeF_.Empty);
            }

            string text = line.text.ToString(pos, 1);

            switch ((int)text [0])
            {
            case '\t':
                if (!line.document.multiline)
                {
                    goto case 10;
                }
                SizeF_ res = TextBoxTextRenderer.MeasureText(dc, " ", font);
                res.Width *= 8.0F;
                return(res);

            case 10:
            case 13:
                return(TextBoxTextRenderer.MeasureText(dc, "\u000D", font));
            }

            return(TextBoxTextRenderer.MeasureText(dc, text, font));
        }
예제 #2
0
        // This doesn't do exactly what you would think, it just pulls off the \n part of the ending
        internal void DrawEnding(Graphics dc, float y)
        {
            if (document.multiline)
            {
                return;
            }
            LineTag last = tags;

            while (last.Next != null)
            {
                last = last.Next;
            }

            string end_str = null;

            switch (document.LineEndingLength(ending))
            {
            case 0:
                return;

            case 1:
                end_str = "\u0013";
                break;

            case 2:
                end_str = "\u0013\u0013";
                break;

            case 3:
                end_str = "\u0013\u0013\u0013";
                break;
            }

            TextBoxTextRenderer.DrawText(dc, end_str, last.Font, last.Color, X + widths [TextLengthWithoutEnding()] - document.viewport_x + document.OffsetX, y, true);
        }
예제 #3
0
 public virtual void Draw(Graphics dc, Color color, float x, float y, int start, int end)
 {
     if (text_position == TextPositioning.Subscript)
     {
         y += OffsetY;
     }
     TextBoxTextRenderer.DrawText(dc, line.text.ToString(start, end).Replace("\r", string.Empty), FontToDisplay, color, x, y, false);
 }
예제 #4
0
        public virtual SizeF SizeOfPosition(Graphics dc, int pos)
        {
            if ((pos >= line.TextLengthWithoutEnding() && line.document.multiline) || !visible)
            {
                return(SizeF.Empty);
            }

            string text = line.text.ToString(pos, 1);

            switch ((int)text [0])
            {
            case '\t':
                if (!line.document.multiline)
                {
                    goto case 10;
                }
                SizeF             res   = TextBoxTextRenderer.MeasureText(dc, " ", FontToDisplay);    // This way we get the height, not that it is ever used...
                float             left  = line.widths [pos];
                float             right = -1;
                TabStopCollection stops = line.tab_stops;
                float             tabPos;
                for (int i = 0; i < stops.Count; i++)
                {
                    tabPos = stops [i].Position;
                    if (tabPos >= left)
                    {
                        if (tabPos <= line.document.viewport_width - line.RightIndent)
                        {
                            break;                             // Can't use tabs that are past the end of the line.
                        }
                        right = stops [i].CalculateRight(line, pos);
                        break;
                    }
                }
                if (right < 0)
                {
                    float maxWidth = dc.DpiX / 2;                     // tab stops are 1/2"
                    right = (float)(Math.Floor(left / maxWidth) + 1) * maxWidth;
                }
                res.Width = right - left;
                return(res);

            case 10:
            case 13:
                return(TextBoxTextRenderer.MeasureText(dc, "\u000D", FontToDisplay));
            }

            return(TextBoxTextRenderer.MeasureText(dc, text, FontToDisplay));
        }
예제 #5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="drawStart">0 based start index</param>
        public virtual void Draw(Graphics dc, Color color, float xoff, float y, int drawStart, int drawEnd,
                                 string text, out Rectangle measuredText, bool measureText)
        {
            if (!visible)
            {
                measuredText = new Rectangle();
                return;
            }

            if (text_position == TextPositioning.Subscript)
            {
                y += OffsetY;
            }

            if (measureText)
            {
                int xstart = (int)line.widths [drawStart] + (int)xoff;
                int xend   = (int)line.widths [drawEnd] - (int)line.widths [drawStart];
                int ystart = (int)y;
                int yend   = (int)TextBoxTextRenderer.MeasureText(dc, Text(), FontToDisplay).Height;

                measuredText = new Rectangle(xstart, ystart, xend, yend);
            }
            else
            {
                measuredText = new Rectangle();
            }

            while (drawStart < drawEnd)
            {
                int tab_index = text.IndexOf("\t", drawStart);

                if (tab_index == -1 || tab_index > drawEnd)
                {
                    tab_index = drawEnd;
                }

                TextBoxTextRenderer.DrawText(dc, text.Substring(drawStart, tab_index - drawStart).Replace("\r", string.Empty), FontToDisplay, color, xoff + line.widths [drawStart], y, false);

                // non multilines get the unknown char
                if (!line.document.multiline && tab_index != drawEnd)
                {
                    TextBoxTextRenderer.DrawText(dc, "\u0013", FontToDisplay, color, xoff + line.widths [tab_index], y, true);
                }

                drawStart = tab_index + 1;
            }
        }
예제 #6
0
        /// <summary>
        /// Recalculate a single line using the same char for every character in the line
        /// </summary>
        internal bool RecalculatePasswordLine(Graphics g, Document doc)
        {
            LineTag tag;
            int     pos;
            int     len;
            float   w;
            bool    ret;

            pos       = 0;
            len       = this.text.Length;
            tag       = this.tags;
            ascent    = 0;
            tag.Shift = 0;

            this.recalc = false;
            widths[0]   = document.left_margin + indent;

            w = TextBoxTextRenderer.MeasureText(g, doc.password_char, tags.Font).Width;

            if (this.textHeight != (int)tag.Font.Height)
            {
                ret = true;
            }
            else
            {
                ret = false;
            }

            this.textHeight = (int)tag.Font.Height;
            tag.Height      = this.textHeight;
            this.height     = (int)(this.LineSpacing + this.TotalParagraphSpacing);

            this.ascent = tag.Ascent;

            while (pos < len)
            {
                pos++;
                widths[pos] = widths[pos - 1] + w;
            }

            return(ret);
        }
예제 #7
0
        /// <summary>
        /// Recalculate a single line using the same char for every character in the line
        /// </summary>
        internal bool RecalculatePasswordLine(Graphics g, Document doc)
        {
            LineTag tag;
            int     pos;
            int     len;
            bool    ret;

            pos       = 0;
            len       = this.text.Length;
            tag       = this.tags;
            ascent    = 0;
            tag.Shift = 0;

            this.recalc = false;
            widths[0]   = document.left_margin + indent;

            if (this.height != (int)tag.Font.Height)
            {
                ret = true;
            }
            else
            {
                ret = false;
            }

            this.height = (int)tag.Font.Height;
            tag.Height  = this.height;

            this.ascent = tag.Ascent;

            while (pos < len)
            {
                pos++;
                string s = new string(doc.password_char[0], len);
                widths[pos] = widths[0] + TextBoxTextRenderer.MeasureText(g, s.Substring(0, pos), tags.Font).Width;
            }

            return(ret);
        }
예제 #8
0
 private void CheckKerning(Graphics g, Font font, int start, int length)
 {
     if (length > 1)
     {
         if (!kerning_fonts.ContainsKey(font.GetHashCode()))
         {
             // Check whether kerning takes place for this string and font.
             var   partText  = text.ToString(start, length);
             var   realSize  = TextBoxTextRenderer.MeasureText(g, partText, font);
             float realWidth = realSize.Width + widths[start + 1];
             // MeasureText ignores trailing whitespace, so we will too at this point.
             int   textLength = partText.TrimEnd().Length;
             float sumWidth   = widths[textLength + start + 1];
             if (realWidth != sumWidth)
             {
                 kerning_fonts.Add(font.GetHashCode(), true);
                 // Using a slightly incorrect width this time around isn't that bad. All that happens
                 // is that the cursor is a pixel or two off until the next character is typed.  It's
                 // the accumulation of pixel after pixel that causes display problems.
             }
         }
     }
 }
예제 #9
0
        private bool RecalculateLine(Graphics g, Document doc, bool handleKerning)
        {
            LineTag tag;
            int     pos;
            int     len;
            SizeF   size;
            float   w;
            int     prev_offset;
            bool    retval;
            bool    wrapped;
            Line    line;
            int     wrap_pos;
            int     prev_height;
            int     prev_ascent;

            pos         = 0;
            len         = this.text.Length;
            tag         = this.tags;
            prev_offset = this.offset;                  // For drawing optimization calculations
            prev_height = this.height;
            prev_ascent = this.ascent;
            this.height = 0;                            // Reset line height
            this.ascent = 0;                            // Reset the ascent for the line
            tag.Shift   = 0;                            // Reset shift (which should be stored as pixels, not as points)

            if (ending == LineEnding.Wrap)
            {
                widths[0] = document.left_margin + hanging_indent;
            }
            else
            {
                widths[0] = document.left_margin + indent;
            }

            this.recalc = false;
            retval      = false;
            wrapped     = false;

            wrap_pos = 0;

            while (pos < len)
            {
                while (tag.Length == 0)                         // We should always have tags after a tag.length==0 unless len==0
                //tag.Ascent = 0;
                {
                    tag.Shift = (tag.Line.ascent - tag.Ascent) / 72;
                    tag       = tag.Next;
                }

                // kerning is a problem.  The original code in this method assumed that the
                // width of a string equals the sum of the widths of its characters.  This is
                // not true when kerning takes place during the display process.  Since it's
                // impossible to find out easily whether a font does kerning, and with which
                // characters, we just detect that kerning must have happened and use a slower
                // (but accurate) measurement for those fonts henceforth.  Without handling
                // kerning, many fonts for English become unreadable during typing for many
                // input strings, and text in many other languages is even worse trying to
                // type in TextBoxes.
                // See https://bugzilla.xamarin.com/show_bug.cgi?id=26478 for details.
                float newWidth;
                if (handleKerning && !Char.IsWhiteSpace(text[pos]))
                {
                    // MeasureText doesn't measure trailing spaces, so we do the best we can for those
                    // in the else branch.
                    size     = TextBoxTextRenderer.MeasureText(g, text.ToString(0, pos + 1), tag.Font);
                    newWidth = widths[0] + size.Width;
                }
                else
                {
                    size     = tag.SizeOfPosition(g, pos);
                    w        = size.Width;
                    newWidth = widths[pos] + w;
                }

                if (Char.IsWhiteSpace(text[pos]))
                {
                    wrap_pos = pos + 1;
                }

                if (doc.wrap)
                {
                    if ((wrap_pos > 0) && (wrap_pos != len) && (newWidth + 5) > (doc.viewport_width - this.right_indent))
                    {
                        // Make sure to set the last width of the line before wrapping
                        widths[pos + 1] = newWidth;

                        pos = wrap_pos;
                        len = text.Length;
                        doc.Split(this, tag, pos);
                        ending = LineEnding.Wrap;
                        len    = this.text.Length;

                        retval  = true;
                        wrapped = true;
                    }
                    else if (pos > 1 && newWidth > (doc.viewport_width - this.right_indent))
                    {
                        // No suitable wrap position was found so break right in the middle of a word

                        // Make sure to set the last width of the line before wrapping
                        widths[pos + 1] = newWidth;

                        doc.Split(this, tag, pos);
                        ending  = LineEnding.Wrap;
                        len     = this.text.Length;
                        retval  = true;
                        wrapped = true;
                    }
                }

                // Contract all wrapped lines that follow back into our line
                if (!wrapped)
                {
                    pos++;

                    widths[pos] = newWidth;

                    if (pos == len)
                    {
                        line = doc.GetLine(this.line_no + 1);
                        if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None))
                        {
                            // Pull the two lines together
                            doc.Combine(this.line_no, this.line_no + 1);
                            len    = this.text.Length;
                            retval = true;
                        }
                    }
                }

                if (pos == (tag.Start - 1 + tag.Length))
                {
                    // We just found the end of our current tag
                    tag.Height = tag.MaxHeight();

                    // Check if we're the tallest on the line (so far)
                    if (tag.Height > this.height)
                    {
                        this.height = tag.Height;                               // Yep; make sure the line knows
                    }
                    if (tag.Ascent > this.ascent)
                    {
                        LineTag t;

                        // We have a tag that has a taller ascent than the line;
                        t = tags;
                        while (t != null && t != tag)
                        {
                            t.Shift = (tag.Ascent - t.Ascent) / 72;
                            t       = t.Next;
                        }

                        // Save on our line
                        this.ascent = tag.Ascent;
                    }
                    else
                    {
                        tag.Shift = (this.ascent - tag.Ascent) / 72;
                    }

                    tag = tag.Next;
                    if (tag != null)
                    {
                        tag.Shift = 0;
                        wrap_pos  = pos;
                    }
                }
            }

            var fullText = text.ToString();

            if (!handleKerning && fullText.Length > 1 && !wrapped)
            {
                // Check whether kerning takes place for this string and font.
                var   realSize  = TextBoxTextRenderer.MeasureText(g, fullText, tags.Font);
                float realWidth = realSize.Width + widths[0];
                // MeasureText ignores trailing whitespace, so we will too at this point.
                int   length   = fullText.TrimEnd().Length;
                float sumWidth = widths[length];
                if (realWidth != sumWidth)
                {
                    kerning_fonts.Add(tags.Font.GetHashCode(), true);
                    // Using a slightly incorrect width this time around isn't that bad. All that happens
                    // is that the cursor is a pixel or two off until the next character is typed.  It's
                    // the accumulation of pixel after pixel that causes display problems.
                }
            }

            while (tag != null)
            {
                tag.Shift = (tag.Line.ascent - tag.Ascent) / 72;
                tag       = tag.Next;
            }

            if (this.height == 0)
            {
                this.height = tags.Font.Height;
                tags.Height = this.height;
                tags.Shift  = 0;
            }

            if (prev_offset != offset || prev_height != this.height || prev_ascent != this.ascent)
            {
                retval = true;
            }

            return(retval);
        }
예제 #10
0
 public virtual void Draw(Graphics dc, Color_ color, float x, float y, int start, int end)
 {
     TextBoxTextRenderer.DrawText(dc, line.text.ToString(start, end).Replace("\r", string.Empty), FontToDisplay, color, x, y, false);
 }
예제 #11
0
        /// <summary>
        /// Go through all tags on a line and recalculate all size-related values;
        /// returns true if lineheight changed
        /// </summary>
        internal bool RecalculateLine(Graphics g, Document doc)
        {
            LineTag tag;
            int     pos;
            int     len;
            Font    currentFont;
            int     currentFontStart;
            SizeF   size;
            float   w;
            int     prev_offset;
            bool    retval;
            bool    wrapped;
            bool    first_in_para;
            Line    line;
            int     wrap_pos;
            int     prev_wrap_pos;
            int     prev_height;
            int     prev_ascent;
            float   prev_spacing_before;
            int     max_above_baseline;
            int     max_below_baseline;
            int     total_ascent;
            int     total_descent;
            TabStop lastTab;
            int     lastTabPos;
            char    c;
            bool    handleKerning;
            float   right_indent;

            pos                 = 0;
            len                 = this.text.Length;
            currentFont         = tags.FontToDisplay;
            currentFontStart    = 0;
            tag                 = this.tags;
            prev_offset         = this.offset;          // For drawing optimization calculations
            prev_height         = this.height;
            prev_ascent         = this.ascent;
            prev_spacing_before = this.SpacingBefore;
            max_above_baseline  = 0;
            max_below_baseline  = 0;
            total_ascent        = 0;
            total_descent       = 0;
            lastTab             = null;
            lastTabPos          = 0;
            this.height         = 0;                              // Reset line height
            this.ascent         = 0;                              // Reset the ascent for the line
            tag.Shift           = 0;                              // Reset shift (which should be stored as pixels, not as points)
            right_indent        = Math.Max(this.right_indent, 0); // Ignore any negative right indent.

            if (line_no > 0)
            {
                line          = doc.GetLine(LineNo - 1);
                first_in_para = line != null && line.ending != LineEnding.Wrap;
            }
            else
            {
                first_in_para = true;
            }

            if (first_in_para)
            {
                widths [0] = indent;
            }
            else
            {
                widths [0] = indent + hanging_indent;
            }

            if (widths [0] < 0)
            {
                widths [0] = 0;                 // Don't allow a negative indent to take the line to a negative position.
            }
            widths [0] += document.left_margin;

            this.recalc = false;
            retval      = false;
            wrapped     = false;

            wrap_pos      = 0;
            prev_wrap_pos = 0;

            handleKerning = kerning_fonts.ContainsKey(currentFont.GetHashCode());

            while (pos < len)
            {
                while (tag.Length == 0)                         // We should always have tags after a tag.length==0 unless len==0
                //tag.Ascent = 0;
                {
                    tag.Shift = (tag.Line.ascent - tag.Ascent);                     // / 72;
                    tag       = tag.Next;
                    if (tag.Length != 0 && tag.FontToDisplay != currentFont)
                    {
                        CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
                        currentFont      = tag.FontToDisplay;
                        currentFontStart = pos;
                        handleKerning    = kerning_fonts.ContainsKey(currentFont.GetHashCode());
                    }
                }

                c = text [pos];

                // kerning is a problem.  The original code in this method assumed that the
                // width of a string equals the sum of the widths of its characters.  This is
                // not true when kerning takes place during the display process.  Since it's
                // impossible to find out easily whether a font does kerning, and with which
                // characters, we just detect that kerning must have happened and use a slower
                // (but accurate) measurement for those fonts henceforth.  Without handling
                // kerning, many fonts for English become unreadable during typing for many
                // input strings, and text in many other languages is even worse trying to
                // type in TextBoxes.
                // See https://bugzilla.xamarin.com/show_bug.cgi?id=26478 for details.
                float newWidth;
                if (handleKerning && !Char.IsWhiteSpace(text[pos]))
                {
                    // MeasureText doesn't measure trailing spaces, so we do the best we can for those
                    // in the else branch.
                    // It doesn't measure /t characters either, we need to add it manually with add_width.
                    size     = TextBoxTextRenderer.MeasureText(g, text.ToString(currentFontStart, pos + 1 - currentFontStart), currentFont);
                    newWidth = widths [currentFontStart] + size.Width;
                }
                else if (c != '\t')
                {
                    size     = tag.SizeOfPosition(g, pos);
                    w        = size.Width;
                    newWidth = widths[pos] + w;
                }
                else
                {
                    CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
                    currentFontStart = pos + 1;                     // Don't try handling the tab along with kerned text.

                    if (lastTab != null)
                    {
                        ProcessLastTab(lastTab, lastTabPos, pos);
                        lastTab = null;
                    }

                    float l = widths [pos];
                    w = -1;
                    for (int i = 0; i < tab_stops.Count; i++)
                    {
                        if (tab_stops [i].Position > l)
                        {
                            lastTab    = tab_stops [i];
                            lastTabPos = pos;
                            w          = lastTab.GetInitialWidth(this, pos);
                            break;
                        }
                    }

                    if (w < 0)
                    {
                        w = tag.SizeOfPosition(g, pos).Width;
                    }

                    newWidth = widths [pos] + w;
                }

                if (doc.Wrap)
                {
                    // FIXME: Technically there are multiple no-break spaces, not just the main one.
                    if ((Char.IsWhiteSpace(c) && c != '\u00A0') || c == '-' || c == '\u2013' || c == '\u2014')
                    {
                        // Primarily break on dashes or whitespace other than a no-break space.
                        prev_wrap_pos = wrap_pos;
                        if (c == '\t')
                        {
                            wrap_pos = pos;                             // Wrap before tabs for some reason.
                        }
                        else
                        {
                            wrap_pos = pos + 1;
                        }
                    }

                    if (newWidth > (doc.viewport_width - this.right_indent))
                    {
                        LineTag split_tag = null;
                        if (wrap_pos > 0)
                        {
                            // Make sure to set the last width of the line before wrapping
                            widths [pos + 1] = newWidth;

                            if (Char.IsWhiteSpace(c))
                            {
                                if (wrap_pos > pos)
                                {
                                    while (wrap_pos < text.Length && Char.IsWhiteSpace(text [wrap_pos]) && text [wrap_pos] != '\t')
                                    {
                                        wrap_pos++;
                                    }
                                    pos++;
                                    wrapped = true;
                                    // don't try pulling more into this line, but keep looping to deal with the rest of the widths and tags
                                }
                            }
                            else
                            {
                                if (wrap_pos > pos && pos > 0)
                                {
                                    // We're at a dash (otherwise we'd be above), but don't have room to fit it in.
                                    // Wrap at the previous wrap point if possible.
                                    wrap_pos = prev_wrap_pos > 0 ? prev_wrap_pos : pos;
                                }
                                split_tag = tag;
                                pos       = wrap_pos;
                            }
                        }
                        else if (pos > 0)
                        {
                            // No suitable wrap position was found so break right in the middle of a word

                            // Make sure to set the last width of the line before wrapping
                            widths [pos + 1] = newWidth;

                            split_tag = tag;
                        }                         // Else don't wrap -- pos == 0, so we'd infinite loop adding blank lines before this.

                        if (split_tag != null)
                        {
                            if (lastTab != null)
                            {
                                ProcessLastTab(lastTab, lastTabPos, pos);
                                lastTab = null;
                            }

                            while (pos < split_tag.Start)
                            {
                                split_tag = split_tag.Previous;
                            }
                            // We have to pass Split the correct tag, and that can change if pos
                            // is set somewhere before the tag change (e.g. by wrap_pos).

                            doc.Split(this, split_tag, pos);
                            ending = LineEnding.Wrap;
                            len    = this.text.Length;

                            retval  = true;
                            wrapped = true;
                        }
                    }
                }

                // Contract all wrapped lines that follow back into our line
                if (!wrapped)
                {
                    pos++;

                    widths[pos] = newWidth;

                    if (pos == len)
                    {
                        line = doc.GetLine(this.line_no + 1);
                        do
                        {
                            if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None) &&
                                (widths[pos] < (doc.viewport_width - this.right_indent) || line.text.Length == 0))
                            {
                                // Pull the two lines together
                                // Only do this if the line isn't already full, or the next line is empty.
                                var h = this.height;                             // Back up h, because Combine sets it to zero.
                                doc.Combine(this, line);
                                this.height = h;                                 // And restore it. There's no point starting at the start again.
                                // Document.Combine() called Line.Streamline(), so it is possible tag points a tag that got removed.
                                tag    = FindTag(pos - 1);                       // So make sure we've got the correct tag.
                                len    = this.text.Length;
                                line   = doc.GetLine(this.line_no + 1);
                                retval = true;
                            }
                        } while ((ending == LineEnding.Wrap || ending == LineEnding.None) && line != null && line.text.Length == 0);
                        // If the next line is empty, do it again (if possible).
                        // The amount of room on this line doesn't matter when there's no text being added...
                    }
                }

                if (pos == (tag.Start - 1 + tag.Length))
                {
                    // We just found the end of our current tag
                    tag.Height = tag.MaxHeight();

                    /* line.ascent is the highest point above the baseline.
                     * total_ascent will equal the maximum distance of the tag above the baseline.
                     * total_descent is needed to calculate the line height.
                     * tag.Shift does not include tag.CharOffset, because Shift puts the tag
                     * on the baseline, while CharOffset moves the baseline.
                     * However, we move the normal baseline when CharOffset is trying to push
                     * stuff off the top.
                     */
                    total_ascent  = tag.Ascent + (int)tag.CharOffset;
                    total_descent = tag.Descent - (int)tag.CharOffset;                     // gets bigger as CharOffset gets smaller
                    if (total_ascent > max_above_baseline)
                    {
                        int moveBy = total_ascent - max_above_baseline;
                        max_above_baseline = total_ascent;

                        LineTag t = tags;
                        while (t != null && t != tag)
                        {
                            t.Shift += moveBy;
                            t        = t.Next;
                        }

                        tag.Shift   = (int)tag.CharOffset;
                        this.ascent = max_above_baseline;
                    }
                    else
                    {
                        tag.Shift = (this.ascent - tag.Ascent);
                    }

                    if (total_descent > max_below_baseline)
                    {
                        max_below_baseline = total_descent;
                    }

                    if (this.height < max_above_baseline + max_below_baseline + tag.Height - tag.Ascent - tag.Descent)
                    {
                        this.height = max_above_baseline + max_below_baseline + tag.Height - tag.Ascent - tag.Descent;
                    }

                    tag = tag.Next;
                    if (tag != null)
                    {
                        if (tag.Length != 0 && tag.FontToDisplay != currentFont)
                        {
                            CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
                            currentFont      = tag.FontToDisplay;
                            currentFontStart = pos;
                            handleKerning    = kerning_fonts.ContainsKey(currentFont.GetHashCode());
                        }
                        tag.Shift = 0;
                        // We can't just wrap on tag boundaries -- e.g. if the first letter of the word has a different colour / font.
                    }
                }
            }

            if (pos != currentFontStart)
            {
                CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
            }

            if (lastTab != null)
            {
                ProcessLastTab(lastTab, lastTabPos, pos);
                lastTab = null;
            }

            while (tag != null)
            {
                tag.Shift = (tag.Line.ascent - tag.Ascent);                 // / 72;
                tag       = tag.Next;
            }

            if (this.height == 0)
            {
                this.height = tags.Font.Height;
                tags.Height = this.height;
                tags.Shift  = 0;
            }

            this.textHeight = this.height;
            this.height     = (int)(this.LineSpacing + this.TotalParagraphSpacing);

            if (prev_offset != offset || prev_height != this.height || prev_ascent != this.ascent ||
                Math.Abs(prev_spacing_before - this.SpacingBefore) > document.Dpi / 1440f)
            {
                retval = true;
            }

            return(retval);
        }