This class represents one line in a paragraph. It is not itself a box, but merely a way of organizing the boxes of the paragraph.
コード例 #1
0
ファイル: ParaBuilder.cs プロジェクト: vkarthim/FieldWorks
        int TopOfNextLine(ParaLine previous, int nextAscent)
        {
            int lineHeight = m_layoutInfo.MpToPixelsY(m_para.Style.LineHeight);
            // where the top has to be if we go by the LineHeight.
            int topNext = previous.Top + previous.Ascent + Math.Abs(lineHeight) - nextAscent;

            if (lineHeight >= 0)
            {
                return(Math.Max(topNext, previous.Bottom));                // don't allow overlap unless doing exact layout.
            }
            return(topNext);
        }
コード例 #2
0
ファイル: ParaBuilder.cs プロジェクト: vkarthim/FieldWorks
        /// <summary>
        /// Compute the height of the box, not counting surrounding gaps, once its lines have been laid out.
        /// </summary>
        int ComputeHeight()
        {
            if (m_lines.Count == 0)
            {
                return(0);
            }
            ParaLine lastLine = m_lines[m_lines.Count - 1];
            var      chrp     = m_para.Style.Chrp;

            m_layoutInfo.VwGraphics.SetupGraphics(ref chrp);
            int bottomOfPara = TopOfNextLine(lastLine, m_layoutInfo.VwGraphics.FontAscent);

            return(bottomOfPara - m_gapTop);
        }
コード例 #3
0
ファイル: ParaBuilder.cs プロジェクト: vkarthim/FieldWorks
        private void BuildALine()
        {
            m_currentLine            = new ParaLine();
            m_spaceLeftOnCurrentLine = m_layoutInfo.MaxWidth - m_surroundWidth;

            if (m_lines.Count == 0)
            {
                m_currentLine.Top         = m_gapTop;
                m_spaceLeftOnCurrentLine -= m_layoutInfo.MpToPixelsY(m_para.Style.FirstLineIndent);
            }
            m_lines.Add(m_currentLine);
            m_lineSegTypes.Clear();
            while (!Finished)
            {
                if (!AddSomethingToLine())
                {
                    break;
                }
            }
            while (!FinalizeLine())
            {
                if (!Backtrack())
                {
                    break;
                }
            }
            if (m_lines.Count > 1)
            {
                ParaLine previous = m_lines[m_lines.Count - 2];
                previous.LastBox.Next = m_currentLine.FirstBox;
                m_currentLine.Top     = TopOfNextLine(previous, m_currentLine.Ascent);
            }
            m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight,
                                       m_lines.Count == 1 ? m_layoutInfo.MpToPixelsY(m_para.Style.FirstLineIndent) : 0,
                                       m_layoutInfo.MaxWidth,
                                       TopDepth);
        }
コード例 #4
0
ファイル: ParaBuilder.cs プロジェクト: sillsdev/FieldWorks
		int TopOfNextLine(ParaLine previous, int nextAscent)
		{
			int lineHeight = m_layoutInfo.MpToPixelsY(m_para.Style.LineHeight);
			// where the top has to be if we go by the LineHeight.
			int topNext = previous.Top + previous.Ascent + Math.Abs(lineHeight) - nextAscent;
			if (lineHeight >= 0)
				return Math.Max(topNext, previous.Bottom); // don't allow overlap unless doing exact layout.
			return topNext;
		}
コード例 #5
0
ファイル: ParaBuilder.cs プロジェクト: sillsdev/FieldWorks
		private void BuildALine()
		{
			m_currentLine = new ParaLine();
			m_spaceLeftOnCurrentLine = m_layoutInfo.MaxWidth - m_surroundWidth;

			if (m_lines.Count == 0)
			{
				m_currentLine.Top = m_gapTop;
				m_spaceLeftOnCurrentLine -= m_layoutInfo.MpToPixelsY(m_para.Style.FirstLineIndent);
			}
			m_lines.Add(m_currentLine);
			m_lineSegTypes.Clear();
			while (!Finished)
				if (!AddSomethingToLine())
					break;
			while (!FinalizeLine())
				if (!Backtrack())
					break;
			if (m_lines.Count > 1)
			{
				ParaLine previous = m_lines[m_lines.Count - 2];
				previous.LastBox.Next = m_currentLine.FirstBox;
				m_currentLine.Top = TopOfNextLine(previous, m_currentLine.Ascent);
			}
			m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight,
									   m_lines.Count == 1 ? m_layoutInfo.MpToPixelsY(m_para.Style.FirstLineIndent) : 0,
									   m_layoutInfo.MaxWidth,
									   TopDepth);
		}
コード例 #6
0
		public void OrderedBoxes()
		{
			var styles = new AssembledStyles();

			// Nothing is reversed when everything is at level 0.
			var line = new ParaLine();
			line.Add(MakeStringBox(styles, 0, false));
			line.Add(MakeStringBox(styles, 0, false));
			line.Add(MakeStringBox(styles, 0, false));
			var boxes = new List<Box>(line.OrderedBoxes(0));
			VerifyBoxOrder(line, boxes, new[] { 0, 1, 2 });

			// Also, three adjacent LTRs in an RTL paragraph are not reversed.
			boxes = new List<Box>(line.OrderedBoxes(1));
			VerifyBoxOrder(line, boxes, new[] { 0, 1, 2 });

			// Everything is reversed when everything is at level 1 (ordinary RTL text).
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 1, false));
			boxes = new List<Box>(line.OrderedBoxes(0));
			VerifyBoxOrder(line, boxes, new[] { 2, 1, 0 });

			// Also, three adjacent RTL boxes in an LTR paragraph are reversed.
			boxes = new List<Box>(line.OrderedBoxes(1));
			VerifyBoxOrder(line, boxes, new[] { 2, 1, 0 });

			// In an RTL paragraph with two adjacent upstream boxes, they preserve their order.
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 1, false));
			boxes = new List<Box>(line.OrderedBoxes(0));
			VerifyBoxOrder(line, boxes, new[] { 3, 1, 2, 0 });

			// In an RTL paragraph with two adjacent upstream boxes, where one is weak, it goes downstream.
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 2, true));
			line.Add(MakeStringBox(styles, 1, false));
			boxes = new List<Box>(line.OrderedBoxes(0));
			VerifyBoxOrder(line, boxes, new[] { 3, 2, 1, 0 });

			// In an RTL paragraph with three adjacent upstream boxes, where the middle one is weak, it goes upstream.
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 2, true));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 1, false));
			boxes = new List<Box>(line.OrderedBoxes(0));
			VerifyBoxOrder(line, boxes, new[] { 4, 1, 2, 3, 0 });
		}
コード例 #7
0
		/// <summary>
		/// Verify the re-ordering of the boxes in the line in the list.
		/// The box at position n in boxes should be the one originally at index[n] in boxes.
		/// </summary>
		private void VerifyBoxOrder(ParaLine line, List<Box> boxes, int[] indexes)
		{
			var original = new List<Box>(line.Boxes);
			for (int i = 0; i < original.Count; i++)
				Assert.That(boxes[i], Is.EqualTo(original[indexes[i]]));
		}
コード例 #8
0
		public void ReverseUpstreamBoxes()
		{
			var line = new ParaLine();
			var styles = new AssembledStyles();

			// no  boxes (pathological)
			line.ReverseUpstreamBoxes(0, new List<Box>(), 0);

			// one box
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 3, false));
			var boxes = new List<Box>(line.Boxes);
			line.ReverseUpstreamBoxes(0, boxes, 0);
			VerifyBoxOrder(line, boxes, new [] {0});

			//  two boxes: should re-order only if depth is small enough.
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 1, false));
			boxes = new List<Box>(line.Boxes);
			line.ReverseUpstreamBoxes(3, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 0, 1 }); // not re-ordered, all depths too small
			line.ReverseUpstreamBoxes(2, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 0, 1 }); // not re-ordered, just one that could be
			line.ReverseUpstreamBoxes(1, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 1, 0 }); // re-ordered, all <= depth
			line.ReverseUpstreamBoxes(0, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 0, 1 }); // re-ordered again, all < depth

			// quite a mixture!
			line = new ParaLine();
			MockDirectionSegment.NextIndex = 0;
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 3, false));
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 4, false));
			line.Add(MakeStringBox(styles, 4, false));
			line.Add(MakeStringBox(styles, 4, false));
			line.Add(MakeStringBox(styles, 4, false));
			line.Add(MakeStringBox(styles, 3, false));
			line.Add(MakeStringBox(styles, 4, false));
			line.Add(MakeStringBox(styles, 5, false));
			line.Add(MakeStringBox(styles, 5, false));
			boxes = new List<Box>(line.Boxes);
			line.ReverseUpstreamBoxes(0, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 11,10,9,8,7,6,5,4,3,2,1,0 }); // reverse everything
			line.ReverseUpstreamBoxes(1, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 0,1,2,3,4,5,6,7,8,9,10,11 }); // back again
			// now the level 1 box at index 2 does not move
			line.ReverseUpstreamBoxes(2, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 1, 0, 2, 11, 10, 9, 8, 7, 6, 5, 4, 3 });
			line.ReverseUpstreamBoxes(2, boxes, 0); // put them back!
			VerifyBoxOrder(line, boxes, new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); // back again
			line.ReverseUpstreamBoxes(4, boxes, 0); // only the groups with more 4 or more reverse
			VerifyBoxOrder(line, boxes, new[] { 0, 1, 2, 3, 7, 6, 5, 4, 8, 11, 10, 9 }); // back again

			boxes = new List<Box>(line.Boxes);
			line.ReverseUpstreamBoxes(1, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 });
			line.ReverseUpstreamBoxes(2, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 3, 4, 5, 6, 7, 8, 9, 10, 11, 2, 0, 1 });
			line.ReverseUpstreamBoxes(3, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 3, 11, 10, 9, 8, 7, 6, 5, 4, 2, 0, 1 });
			line.ReverseUpstreamBoxes(4, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 3, 9, 10, 11, 8, 4, 5, 6, 7, 2, 0, 1 });
			line.ReverseUpstreamBoxes(5, boxes, 0);
			VerifyBoxOrder(line, boxes, new[] { 3, 9, 11, 10, 8, 4, 5, 6, 7, 2, 0, 1 });

			boxes = new List<Box>(line.OrderedBoxes(0));
			// This should do all in one step the reversals indicated in the previous test
			// sequence. The results above indicate how the final sequence is arrived at.
			VerifyBoxOrder(line, boxes, new[] { 3, 9, 11, 10, 8, 4, 5, 6, 7, 2, 0, 1 });

			line = new ParaLine();
			line.Add(new BlockBox(styles, Color.Red, 200, 300));
			line.Add(MakeStringBox(styles, 2, true));
			line.Add(MakeStringBox(styles, 2, true));
			line.Add(new BlockBox(styles, Color.Red, 200, 300));
			boxes = new List<Box>(line.Boxes);
			line.ReverseUpstreamBoxes(2, boxes, 1);
			VerifyBoxOrder(line, boxes, new[] { 0,  2, 1, 3}); // reverse just the string boxes
		}
コード例 #9
0
		private void VerifyDepth(ParaLine line, int index, int expected)
		{
			int depth;
			((StringBox) line.Boxes.Skip(index).First()).Segment.get_DirectionDepth(0, out depth);
			Assert.That(depth, Is.EqualTo(expected));
		}
コード例 #10
0
		public void SetWeakDirections()
		{
			var line = new ParaLine();
			var styles = new AssembledStyles();

			// no  boxes (pathological)
			line.SetWeakDirections(0);

			// no weak boxes
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 3, false));
			line.SetWeakDirections(0);
			VerifyDepth(line, 0, 3);

			// one weak box alone: no change
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 3, true));
			line.SetWeakDirections(1);
			VerifyDepth(line, 0, 1);

			//  one at start, followed by a non-weak
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 2, true));
			line.Add(MakeStringBox(styles, 1, false));
			line.SetWeakDirections(0);
			VerifyDepth(line, 0, 0); // adjacent to paragraph boundary, topDepth wins
			line.SetWeakDirections(2);
			VerifyDepth(line, 0, 1); // adjacent box has lower depth.

			// two at start, followed by non-weak; also two at end and four in middle.
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 2, true));
			line.Add(MakeStringBox(styles, 3, true));
			line.Add(MakeStringBox(styles, 1, false));
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 4, true));
			line.Add(MakeStringBox(styles, 4, true));
			line.Add(MakeStringBox(styles, 4, true));
			line.Add(MakeStringBox(styles, 4, true));
			line.Add(MakeStringBox(styles, 3, false));
			line.Add(MakeStringBox(styles, 4, false));
			line.Add(MakeStringBox(styles, 5, true));
			line.Add(MakeStringBox(styles, 5, true));
			line.SetWeakDirections(6); // let the adjacent boxes rather than the paragraph depth win.
			VerifyDepth(line, 0, 1); // first two set to depth of following box
			VerifyDepth(line, 1, 1);
			VerifyDepth(line, 4, 2); // middle four set to depth of preceding box
			VerifyDepth(line, 5, 2);
			VerifyDepth(line, 6, 2);
			VerifyDepth(line, 7, 2);
			VerifyDepth(line, 10, 4); // last two set to depth of preceding
			VerifyDepth(line, 11, 4);
			line.SetWeakDirections(0); // let the adjacent boxes rather than the paragraph depth win.
			VerifyDepth(line, 0, 0); // topdepth from para boundary
			VerifyDepth(line, 1, 0);
			VerifyDepth(line, 4, 2); // middle four set to depth of preceding box
			VerifyDepth(line, 5, 2);
			VerifyDepth(line, 6, 2);
			VerifyDepth(line, 7, 2);
			VerifyDepth(line, 10, 0); // topdepth from para boundary
			VerifyDepth(line, 11, 0);

			line= new ParaLine();
			line.Add(MakeStringBox(styles, 2, false));
			line.Add(MakeStringBox(styles, 3, true));
			line.Add(new BlockBox(styles, Color.Red, 200, 300));
			line.SetWeakDirections(1); // The block box is considered to have depth 1 and wins
			VerifyDepth(line, 1, 1);
		}
コード例 #11
0
		public void ArrangeBoxes()
		{
			var styles = new AssembledStyles();

			// Ordinary layout
			var line = new ParaLine();
			line.Add(MakeStringBox(styles, 0, false));
			var box2 = MakeStringBox(styles, 0, false);
			line.Add(box2);
			line.Add(MakeStringBox(styles, 0, false));
			((MockDirectionSegment) box2.Segment).SimulatedWidth = 20;
			var layoutInfo = MakeLayoutInfo();
			foreach (var box in line.Boxes)
				box.Layout(layoutInfo);
			line.ArrangeBoxes(FwTextAlign.ktalLeft, 7, 0, 0, 100, 0);
			Assert.That(line.Boxes.First().Left, Is.EqualTo(7));
			Assert.That(line.Boxes.Skip(1).First().Left, Is.EqualTo(17)); // past first default(10)-width box
			Assert.That(line.Boxes.Skip(2).First().Left, Is.EqualTo(37)); // past second, 20-pixel box.

			// Still LTR, but aligned right
			line.ArrangeBoxes(FwTextAlign.ktalRight, 7, 3, 10, 100, 0);
			Assert.That(line.Boxes.Skip(2).First().Left, Is.EqualTo(100 - 3 - 10)); // from maxwidth, minus gapright, minus 10 pix width.
			Assert.That(line.Boxes.Skip(1).First().Left, Is.EqualTo(100 - 3 - 10 - 20)); // further left by 20 pix width of middle default-width box
			Assert.That(line.Boxes.First().Left, Is.EqualTo(100 -3 - 10 - 20 - 10)); // still further by 10 pix width of first box

			// Still LTR, but aligned center
			line.ArrangeBoxes(FwTextAlign.ktalCenter, 7, 3, 10, 100, 0);
			int sumBoxWidth = 40;
			int available = 100 - 7 - 3 - 10; // the width in which we can center
			int start = 7 + 10 + (available - sumBoxWidth)/2;
			Assert.That(line.Boxes.First().Left, Is.EqualTo(start));
			Assert.That(line.Boxes.Skip(1).First().Left, Is.EqualTo(start + 10)); // past first default(10)-width box
			Assert.That(line.Boxes.Skip(2).First().Left, Is.EqualTo(start + 30)); // past second, 20-pixel box.

			// Enhance: add test for justified.

			// Now simulate RTL paragraph with similar contents. Boxes are in opposite order.
			line = new ParaLine();
			line.Add(MakeStringBox(styles, 1, false));
			box2 = MakeStringBox(styles, 1, false);
			line.Add(box2);
			line.Add(MakeStringBox(styles, 1, false));
			((MockDirectionSegment)box2.Segment).SimulatedWidth = 30;
			foreach (var box in line.Boxes)
				box.Layout(layoutInfo);
			line.ArrangeBoxes(FwTextAlign.ktalLeft, 7, 0, 0, 100, 1);
			Assert.That(line.Boxes.Skip(2).First().Left, Is.EqualTo(7)); // last box is now leftmost.
			Assert.That(line.Boxes.Skip(1).First().Left, Is.EqualTo(17)); // second is left of first by width of first
			Assert.That(line.Boxes.First().Left, Is.EqualTo(47)); // first is now on the right.

			// Verify that it works correctly for align right (obey firstLineIndent!) and center.
			line.ArrangeBoxes(FwTextAlign.ktalRight, 7, 3, 15, 100, 1);
			var rightOfFirstBox = 100 - 3 - 15;
			Assert.That(line.Boxes.First().Right, Is.EqualTo(rightOfFirstBox));
			Assert.That(line.Boxes.Skip(1).First().Right, Is.EqualTo(rightOfFirstBox - 10)); // past first default-width box
			Assert.That(line.Boxes.Skip(2).First().Right, Is.EqualTo(rightOfFirstBox - 10 - 30)); // past second, 30-pixel box.
		}
コード例 #12
0
ファイル: ParaBuilder.cs プロジェクト: vkarthim/FieldWorks
        /// <summary>
        /// Redo layout. Should produce the same segments as FullLayout, but assume that segments for text up to
        /// details.StartChange may be reused (if not affected by changing line breaks), and segments after
        /// details.StartChange+details.DeleteCount may be re-used if a line break works out (and after adjusting
        /// their begin offset).
        /// </summary>
        /// <param name="details"></param>
        internal void Relayout(SourceChangeDetails details, LayoutCallbacks lcb)
        {
            m_reuseableLines = m_para.Lines;
            m_lines          = new List <ParaLine>();
            m_renderRunIndex = 0;
            m_ichRendered    = 0;
            IRenderRun last = m_renderRuns[m_renderRuns.Count - 1];

            m_ichLim             = last.RenderStart + last.RenderLength;
            m_lastRenderRunIndex = m_renderRuns.Count;
            Rectangle invalidateRect = m_para.InvalidateRect;
            int       delta          = details.InsertCount - details.DeleteCount;
            int       oldHeight      = m_para.Height;
            int       oldWidth       = m_para.Width;

            // Make use of details.StartChange to reuse some lines at start.
            if (m_reuseableLines.Count > 0)
            {
                // As long as we have two complete lines before the change, we can certainly reuse the first of them.
                while (m_reuseableLines.Count > 2 && details.StartChange > m_reuseableLines[2].IchMin)
                {
                    m_lines.Add(m_reuseableLines[0]);
                    m_reuseableLines.RemoveAt(0);
                }
                // If we still have one complete line before the change, we can reuse it provided there is white
                // space after the end of the line and before the change.
                if (m_reuseableLines.Count > 1)
                {
                    int startNextLine = m_reuseableLines[1].IchMin;
                    if (details.StartChange > startNextLine)
                    {
                        bool   fGotWhite = false;
                        string line1Text = m_reuseableLines[1].CheckedText;
                        int    lim       = details.StartChange - startNextLine;
                        var    cpe       = LgIcuCharPropEngineClass.Create();
                        for (int ich = 0; ich < lim; ich++)
                        {
                            // Enhance JohnT: possibly we need to consider surrogates here?
                            // Worst case is we don't reuse a line we could have, since a surrogate won't
                            // be considered white.
                            if (cpe.get_IsSeparator(Convert.ToInt32(line1Text[ich])))
                            {
                                fGotWhite = true;
                                break;
                            }
                        }
                        if (fGotWhite)
                        {
                            m_lines.Add(m_reuseableLines[0]);
                            m_reuseableLines.RemoveAt(0);
                        }
                    }
                }
                m_ichRendered = m_reuseableLines[0].IchMin;
                int topOfFirstDiscardedLine = m_reuseableLines[0].Top;
                // We don't need to invalidate the lines we're keeping.
                invalidateRect = new Rectangle(invalidateRect.Left, invalidateRect.Top + topOfFirstDiscardedLine,
                                               invalidateRect.Width, invalidateRect.Height - topOfFirstDiscardedLine);
            }

            // Figure out which run we need to continue from, to correspond to the start of the first line
            // we need to rebuild.
            while (m_renderRunIndex < m_renderRuns.Count && m_renderRuns[m_renderRunIndex].RenderLim <= m_ichRendered)
            {
                m_renderRunIndex++;
            }

            while (!Finished)
            {
                // Todo: I think we need to adjust available width if this is the first line.
                BuildALine();
                // Drop any initial reusable lines we now determine to be unuseable after all.
                // If we've used characters beyond the start of this potentially reusable line, we can't reuse it.
                // Also, we don't reuse empty lines. Typically an empty line is left over from a previously empty
                // paragraph, and we no longer need the empty segment, even though it doesn't have any of the same
                // characters (since it has none) as the segment that has replaced it.
                while (m_reuseableLines.Count > 0 && (m_ichRendered > m_reuseableLines[0].IchMin + delta || m_reuseableLines[0].Length == 0))
                {
                    m_reuseableLines.RemoveAt(0);
                }
                if (m_reuseableLines.Count > 0)
                {
                    // See if we can resync.
                    var nextLine = m_reuseableLines[0];
                    if (m_ichRendered == nextLine.IchMin + delta)
                    {
                        // reuse it.
                        int top = m_gapTop;
                        if (m_lines.Count > 0)
                        {
                            ParaLine previous = m_lines.Last();
                            previous.LastBox.Next = nextLine.FirstBox;
                            top = TopOfNextLine(previous, nextLine.Ascent);
                        }

                        m_lines.AddRange(m_reuseableLines);
                        if (top != nextLine.Top)
                        {
                            ParaLine previous = null;
                            foreach (var line in m_reuseableLines)
                            {
                                if (previous != null)                                 // first time top has already been computed
                                {
                                    top = TopOfNextLine(previous, line.Ascent);
                                }
                                line.Top = top;                                 // BEFORE ArrangeBoxes, since it gets copied to the individual boxes
                                m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight, 0, m_layoutInfo.MaxWidth, TopDepth);
                                previous = line;
                            }
                        }
                        else
                        {
                            // reusable lines have not moved, we don't need to invalidate them.
                            invalidateRect.Height -= (m_reuseableLines.Last().Bottom - top);
                        }
                        for (Box box = nextLine.FirstBox; box != null; box = box.Next)
                        {
                            if (box is StringBox)
                            {
                                (box as StringBox).IchMin += delta;
                            }
                        }

                        break;
                    }
                }
            }
            SetParaInfo();
            // if the paragraph got larger, we need to invalidate the extra area.
            // (But, don't reduce it if it got smaller; we want to invalidate all the old stuff as well as all the new.)
            if (m_para.Height > oldHeight)
            {
                invalidateRect.Height += m_para.Height - oldHeight;
            }
            if (m_para.Width > oldWidth)
            {
                invalidateRect.Width += m_para.Width - oldWidth;
            }
            lcb.InvalidateInRoot(invalidateRect);
        }