/// <summary>
            /// Writes the specified metadata to the <see cref="TextLayoutCommandType.BlockInfo"/> command for this text.
            /// </summary>
            /// <param name="output">The command stream to which commands are being written.</param>
            /// <param name="blockWidth">The width of the block in pixels.</param>
            /// <param name="blockHeight">The height of the block in pixels.</param>
            /// <param name="lengthInLines">The length of the block of text in lines.</param>
            /// <param name="settings">The layout settings.</param>
            public void WriteBlockInfo(TextLayoutCommandStream output, Int32 blockWidth, Int32 blockHeight, Int32 lengthInLines, ref TextLayoutSettings settings)
            {
                var offset = 0;

                if (settings.Height.HasValue)
                {
                    if ((settings.Flags & TextFlags.AlignBottom) == TextFlags.AlignBottom)
                    {
                        offset = (settings.Height.Value - blockHeight);
                    }
                    else if ((settings.Flags & TextFlags.AlignMiddle) == TextFlags.AlignMiddle)
                    {
                        offset = (settings.Height.Value - blockHeight) / 2;
                    }
                }

                output.Seek(0);
                unsafe
                {
                    var ptr = (TextLayoutBlockInfoCommand *)output.Data;
                    ptr->Offset        = offset;
                    ptr->LengthInLines = lengthInLines;
                }
                output.Seek(output.Count);

                minBlockOffset = (minBlockOffset.HasValue) ? Math.Min(minBlockOffset.Value, offset) : offset;
            }
            /// <summary>
            /// Writes the specified metadata to the <see cref="TextLayoutCommandType.LineInfo"/> command for this text.
            /// </summary>
            /// <param name="output">The command stream to which commands are being written.</param>
            /// <param name="lineWidth">The width of the line in pixels.</param>
            /// <param name="lineHeight">The height of the line in pixels.</param>
            /// <param name="lengthInCommands">The length of the line of text in commands.</param>
            /// <param name="lengthInGlyphs">The length of the line of text in glyphs.</param>
            /// <param name="terminatedByLineBreak">A value indicating whether the line is terminated by a line break.</param>
            /// <param name="settings">The layout settings.</param>
            public void WriteLineInfo(TextLayoutCommandStream output,
                                      Int32 lineWidth, Int32 lineHeight, Int32 lengthInCommands, Int32 lengthInGlyphs, Boolean terminatedByLineBreak, ref TextLayoutSettings settings)
            {
                var offset = 0;

                if (settings.Width.HasValue)
                {
                    if ((settings.Flags & TextFlags.AlignRight) == TextFlags.AlignRight)
                    {
                        offset = (settings.Width.Value - lineWidth);
                    }
                    else if ((settings.Flags & TextFlags.AlignCenter) == TextFlags.AlignCenter)
                    {
                        offset = (settings.Width.Value - lineWidth) / 2;
                    }
                }

                var outputStreamPosition = output.StreamPositionInObjects;

                output.Seek(lineInfoCommandIndex);
                unsafe
                {
                    var ptr = (TextLayoutLineInfoCommand *)output.Data;
                    ptr->Offset                = offset;
                    ptr->LineWidth             = lineWidth;
                    ptr->LineHeight            = lineHeight;
                    ptr->LengthInCommands      = lengthInCommands;
                    ptr->LengthInGlyphs        = lengthInGlyphs;
                    ptr->TerminatedByLineBreak = terminatedByLineBreak;
                }
                output.Seek(outputStreamPosition);

                minLineOffset = (minLineOffset.HasValue) ? Math.Min(minLineOffset.Value, offset) : offset;
            }
            /// <summary>
            /// Finalizes the layout by writing the block's metadata to the command stream.
            /// </summary>
            /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
            /// <param name="settings">The current layout settings.</param>
            public void FinalizeLayout(TextLayoutCommandStream output, ref TextLayoutSettings settings)
            {
                if (LineHeightTentative > 0 || LineHeight > 0)
                {
                    FinalizeLine(output, ref settings);
                }

                WriteBlockInfo(output, ActualWidth, ActualHeight, LineCount, ref settings);

                output.Settings     = settings;
                output.Bounds       = Bounds;
                output.ActualWidth  = ActualWidth;
                output.ActualHeight = ActualHeight;
                output.TotalLength  = TotalLength;
                output.LineCount    = LineCount;

                if (!settings.Width.HasValue)
                {
                    if ((settings.Flags & TextFlags.AlignCenter) == TextFlags.AlignCenter ||
                        (settings.Flags & TextFlags.AlignRight) == TextFlags.AlignRight)
                    {
                        FixHorizontalAlignmentForUnconstrainedLayout(output, ref settings);
                    }
                }
            }
            /// <summary>
            /// Finalizes the current line by writing the line's metadata to the command stream and resetting
            /// state values which are associated with the current line.
            /// </summary>
            /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
            /// <param name="settings">The current layout settings.</param>
            public void FinalizeLine(TextLayoutCommandStream output, ref TextLayoutSettings settings)
            {
                if (LineHeight == 0)
                {
                    LineHeight = LineHeightTentative;
                }

                WriteLineInfo(output, LineWidth, LineHeight, LineLengthInCommands,
                              LineLengthInGlyphs, LineLengthInSource, LineLengthInShaped, terminatingLineBreakLength ?? 0, ref settings);

                PositionX     = 0;
                PositionY    += LineHeight;
                ActualWidth   = Math.Max(ActualWidth, LineWidth);
                ActualHeight += LineHeight;
                LineCount++;
                LineWidth                  = 0;
                LineHeight                 = 0;
                LineHeightTentative        = 0;
                LineLengthInGlyphs         = 0;
                LineLengthInSource         = 0;
                LineLengthInShaped         = 0;
                LineLengthInCommands       = 0;
                LineInfoCommandIndex       = output.Count;
                LineBreakCommand           = null;
                LineBreakOffsetInput       = null;
                LineBreakOffsetOutput      = null;
                terminatingLineBreakLength = null;
                BrokenTextSizeBeforeBreak  = null;
                BrokenTextSizeAfterBreak   = null;
            }
            /// <summary>
            /// Finalizes the current line by writing the line's metadata to the command stream and resetting
            /// state values which are associated with the current line.
            /// </summary>
            /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
            /// <param name="settings">The current layout settings.</param>
            public void FinalizeLine(TextLayoutCommandStream output, ref TextLayoutSettings settings)
            {
                if (lineHeight == 0)
                {
                    lineHeight = lineHeightTentative;
                }

                WriteLineInfo(output, lineWidth, lineHeight, lineLengthInCommands, lineLengthInText, lineIsTerminatedByLineBreak, ref settings);

                positionX     = 0;
                positionY    += lineHeight;
                actualWidth   = Math.Max(actualWidth, lineWidth);
                actualHeight += lineHeight;
                lineCount++;
                lineWidth                   = 0;
                lineHeight                  = 0;
                lineHeightTentative         = 0;
                lineLengthInText            = 0;
                lineLengthInCommands        = 0;
                lineInfoCommandIndex        = output.Count;
                lineBreakCommand            = null;
                lineBreakOffset             = null;
                lineIsTerminatedByLineBreak = false;
                brokenTextSizeBeforeBreak   = null;
                brokenTextSizeAfterBreak    = null;
            }
Esempio n. 6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LineInfo"/> structure.
 /// </summary>
 /// <param name="source">The command stream from which this line was retrieved.</param>
 /// <param name="lineIndex">The index of the line within its layout.</param>
 /// <param name="offsetInCommands">The index of the command that contains the line's metadata.</param>
 /// <param name="offsetInGlyphs">The index of the first glyph in the line.</param>
 /// <param name="x">The x-coordinate of the line's top-left corner relative to its layout area.</param>
 /// <param name="y">The y-coordinate of the line's top-left corner relative to its layout area.</param>
 /// <param name="width">The line's width in pixels.</param>
 /// <param name="height">The line's height in pixels.</param>
 /// <param name="lengthInCommands">The line's length in commands.</param>
 /// <param name="lengthInGlyphs">The line's length in glyphs.</param>
 internal LineInfo(TextLayoutCommandStream source, Int32 lineIndex, Int32 offsetInCommands, Int32 offsetInGlyphs,
                   Int32 x, Int32 y, Int32 width, Int32 height, Int32 lengthInCommands, Int32 lengthInGlyphs)
 {
     this.source           = source;
     this.lineIndex        = lineIndex;
     this.offsetInCommands = offsetInCommands;
     this.offsetInGlyphs   = offsetInGlyphs;
     this.x                = x;
     this.y                = y;
     this.width            = width;
     this.height           = height;
     this.lengthInCommands = lengthInCommands;
     this.lengthInGlyphs   = lengthInGlyphs;
 }
Esempio n. 7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LineInfo"/> structure.
 /// </summary>
 /// <param name="source">The command stream from which this line was retrieved.</param>
 /// <param name="lineIndex">The index of the line within its layout.</param>
 /// <param name="offsetInCommands">The index of the command that contains the line's metadata.</param>
 /// <param name="offsetInSource">The line's offset within the source string.</param>
 /// <param name="offsetInGlyphs">The line's offset within the glyph buffer (or the source string if the text is not shaped).</param>
 /// <param name="x">The x-coordinate of the line's top-left corner relative to its layout area.</param>
 /// <param name="y">The y-coordinate of the line's top-left corner relative to its layout area.</param>
 /// <param name="width">The line's width in pixels.</param>
 /// <param name="height">The line's height in pixels.</param>
 /// <param name="lengthInCommands">The line's length in commands.</param>
 /// <param name="lengthInSource">The line's length in source characters.</param>
 /// <param name="lengthInGlyphs">The line's length in glyphs.</param>
 /// <param name="terminatingLineBreakLength">The length of the line's terminating line break in the source text.</param>
 internal LineInfo(TextLayoutCommandStream source, Int32 lineIndex, Int32 offsetInCommands, Int32 offsetInSource, Int32 offsetInGlyphs,
                   Int32 x, Int32 y, Int32 width, Int32 height, Int32 lengthInCommands, Int32 lengthInSource, Int32 lengthInGlyphs, Int32 terminatingLineBreakLength)
 {
     this.Source           = source;
     this.LineIndex        = lineIndex;
     this.OffsetInCommands = offsetInCommands;
     this.OffsetInSource   = offsetInSource;
     this.OffsetInGlyphs   = offsetInGlyphs;
     this.X                          = x;
     this.Y                          = y;
     this.Width                      = width;
     this.Height                     = height;
     this.LengthInCommands           = lengthInCommands;
     this.LengthInSource             = lengthInSource;
     this.LengthInGlyphs             = lengthInGlyphs;
     this.TerminatingLineBreakLength = terminatingLineBreakLength;
 }
            /// <summary>
            /// This method corrects the offsets of lines in a layout which is right- or center-aligned but which
            /// did not have a constrained horizontal layout space.
            /// </summary>
            private unsafe void FixHorizontalAlignmentForUnconstrainedLayout(TextLayoutCommandStream output, ref TextLayoutSettings settings)
            {
                output.Seek(0);

                while (output.SeekNextLine())
                {
                    var lineInfo = (TextLayoutLineInfoCommand *)output.InternalObjectStream.Data;

                    if ((settings.Flags & TextFlags.AlignRight) == TextFlags.AlignRight)
                    {
                        lineInfo->Offset = (output.ActualWidth - lineInfo->LineWidth);
                    }
                    else if ((settings.Flags & TextFlags.AlignCenter) == TextFlags.AlignCenter)
                    {
                        lineInfo->Offset = (output.ActualWidth - lineInfo->LineWidth) / 2;
                    }
                }
            }
            /// <summary>
            /// Advances the layout state to the next line of text after inserting a line break character at the end of the current line.
            /// </summary>
            /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
            /// <param name="length">The number of characters in the line break.</param>
            /// <param name="settings">The current layout settings.</param>
            public void AdvanceLayoutToNextLineWithBreak(TextLayoutCommandStream output, Int32 length, ref TextLayoutSettings settings)
            {
                var lineSpacing = settings.Font.GetFace(UltravioletFontStyle.Regular).LineSpacing;

                var lineHeightCurrent = lineHeight;

                if (lineHeightCurrent == 0)
                {
                    lineHeightCurrent = lineSpacing;
                }

                output.WriteLineBreak(new TextLayoutLineBreakCommand(length));
                AdvanceLineToNextCommand(0, lineHeightCurrent, 1, length, isLineBreak: true);

                AdvanceLayoutToNextLine(output, ref settings);
                AdvanceLineToNextCommand(0, 0, 0, 0);

                lineHeightTentative = lineSpacing;
            }
            /// <summary>
            /// Replaces the last breaking space on the current line with a line break.
            /// </summary>
            /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
            /// <param name="settings">The current layout settings.</param>
            public unsafe Boolean ReplaceLastBreakingSpaceWithLineBreak(TextLayoutCommandStream output, ref TextLayoutSettings settings)
            {
                if (!lineBreakCommand.HasValue || !lineBreakOffset.HasValue)
                {
                    return(false);
                }

                var sizeBeforeBreak     = brokenTextSizeBeforeBreak.Value;
                var sizeAfterBreak      = brokenTextSizeAfterBreak.Value;
                var brokenCommandSize   = Size2.Zero;
                var brokenCommandOffset = 0;
                var brokenCommandLength = 0;

                var newLineHeight = sizeAfterBreak.Height;

                if (newLineHeight == 0)
                {
                    newLineHeight = settings.Font.GetFace(UltravioletFontStyle.Regular).LineSpacing;
                }

                // Truncate the command which is being broken.
                output.Seek(lineBreakCommand.Value);
                unsafe
                {
                    var cmd = (TextLayoutTextCommand *)output.Data;

                    brokenCommandOffset = cmd->TextOffset;
                    brokenCommandLength = cmd->TextLength;
                    brokenCommandSize   = cmd->Bounds.Size;

                    cmd->TextLength = lineBreakOffset.Value;
                    cmd->TextWidth  = (Int16)sizeBeforeBreak.Width;
                    cmd->TextHeight = (Int16)sizeBeforeBreak.Height;
                }
                output.SeekNextCommand();

                // Insert a line break, a new line, and the second half of the truncated text.
                var part1Length          = lineBreakOffset.Value;
                var part2Offset          = brokenCommandOffset + (lineBreakOffset.Value + 1);
                var part2Length          = brokenCommandLength - (part1Length + 1);
                var part2IsNotDegenerate = (part2Length > 0);

                var numberOfObjects = part2IsNotDegenerate ? 3 : 2;
                var numberOfBytes   =
                    sizeof(TextLayoutLineBreakCommand) +
                    sizeof(TextLayoutLineInfoCommand) +
                    (part2IsNotDegenerate ? sizeof(TextLayoutTextCommand) : 0);

                var insertionPosition = output.InternalObjectStream.PositionInObjects;

                output.InternalObjectStream.ReserveInsert(numberOfObjects, numberOfBytes);

                *(TextLayoutLineBreakCommand *)output.Data = new TextLayoutLineBreakCommand(1);
                output.InternalObjectStream.FinalizeObject(sizeof(TextLayoutLineBreakCommand));

                *(TextLayoutCommandType *)output.Data = TextLayoutCommandType.LineInfo;
                output.InternalObjectStream.FinalizeObject(sizeof(TextLayoutLineInfoCommand));

                if (part2IsNotDegenerate)
                {
                    var textOffset = part2Offset;
                    var textLength = part2Length;

                    *(TextLayoutTextCommand *)output.InternalObjectStream.Data = new TextLayoutTextCommand(textOffset, textLength,
                                                                                                           0, positionY + lineHeight, (Int16)sizeAfterBreak.Width, (Int16)sizeAfterBreak.Height);
                    output.InternalObjectStream.FinalizeObject(sizeof(TextLayoutTextCommand));
                }

                // Add the line break command to the broken line.
                AdvanceLineToNextCommand(0, 0, 1, 1);

                // Recalculate the parameters for the broken line.
                output.Seek(LineInfoCommandIndex + 1);

                var brokenLineWidth            = 0;
                var brokenLineHeight           = 0;
                var brokenLineLengthInText     = 0;
                var brokenLineLengthInCommands = 0;

                var cmdType = TextLayoutCommandType.None;

                while ((cmdType = *(TextLayoutCommandType *)output.Data) != TextLayoutCommandType.LineInfo)
                {
                    switch (cmdType)
                    {
                    case TextLayoutCommandType.Text:
                    {
                        var cmd = (TextLayoutTextCommand *)output.Data;
                        brokenLineWidth        += cmd->TextWidth;
                        brokenLineHeight        = Math.Max(brokenLineHeight, cmd->TextHeight);
                        brokenLineLengthInText += cmd->TextLength;
                    }
                    break;

                    case TextLayoutCommandType.Icon:
                    {
                        var cmd = (TextLayoutIconCommand *)output.Data;
                        brokenLineWidth        += cmd->Bounds.Width;
                        brokenLineHeight        = Math.Max(brokenLineHeight, cmd->Bounds.Height);
                        brokenLineLengthInText += 1;
                    }
                    break;

                    case TextLayoutCommandType.LineBreak:
                    {
                        var cmd = (TextLayoutLineBreakCommand *)output.Data;
                        brokenLineLengthInText += cmd->Length;
                    }
                    break;
                    }
                    brokenLineLengthInCommands++;
                    output.SeekNextCommand();
                }

                // Finalize the broken line.
                totalLength          = (totalLength - lineLengthInText) + brokenLineLengthInText;
                lineWidth            = brokenLineWidth;
                lineHeight           = brokenLineHeight;
                lineLengthInText     = brokenLineLengthInText;
                lineLengthInCommands = brokenLineLengthInCommands;
                FinalizeLine(output, ref settings);

                // Fixup token bounds and update parameters for new line.
                LineInfoCommandIndex = insertionPosition + 1;
                while (output.StreamPositionInObjects < output.Count)
                {
                    var width            = 0;
                    var height           = 0;
                    var lengthInCommands = 0;
                    var lengthInText     = 0;

                    switch (*(TextLayoutCommandType *)output.Data)
                    {
                    case TextLayoutCommandType.Text:
                    {
                        var cmd = (TextLayoutTextCommand *)output.Data;
                        width            = cmd->TextWidth;
                        height           = cmd->TextHeight;
                        lengthInCommands = 1;
                        lengthInText     = cmd->TextLength;
                        cmd->TextX       = PositionX;
                        cmd->TextY       = PositionY;
                    }
                    break;

                    case TextLayoutCommandType.Icon:
                    {
                        var cmd = (TextLayoutIconCommand *)output.Data;
                        width            = cmd->IconWidth;
                        height           = cmd->IconHeight;
                        lengthInCommands = 1;
                        lengthInText     = 1;
                        cmd->IconX       = PositionX;
                        cmd->IconY       = PositionY;
                    }
                    break;

                    case TextLayoutCommandType.LineBreak:
                    {
                        var cmd = (TextLayoutLineBreakCommand *)output.Data;
                        lengthInText += cmd->Length;
                    }
                    break;
                    }

                    AdvanceLineToNextCommand(width, height, lengthInCommands, lengthInText);
                    output.SeekNextCommand();
                }

                return(true);
            }
 /// <summary>
 /// Advances the layout state to the next line of text.
 /// </summary>
 /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
 /// <param name="settings">The current layout settings.</param>
 public void AdvanceLayoutToNextLine(TextLayoutCommandStream output, ref TextLayoutSettings settings)
 {
     FinalizeLine(output, ref settings);
     output.WriteLineInfo();
 }
            /// <summary>
            /// Replaces the last breaking space on the current line with a line break.
            /// </summary>
            /// <param name="output">The <see cref="TextLayoutCommandStream"/> which is being populated.</param>
            /// <param name="settings">The current layout settings.</param>
            public unsafe Boolean ReplaceLastBreakingSpaceWithLineBreak(TextLayoutCommandStream output, ref TextLayoutSettings settings)
            {
                if (!LineBreakCommand.HasValue || !LineBreakOffsetInput.HasValue || !LineBreakOffsetOutput.HasValue)
                {
                    return(false);
                }

                var sizeBeforeBreak           = BrokenTextSizeBeforeBreak.Value;
                var sizeAfterBreak            = BrokenTextSizeAfterBreak.Value;
                var brokenCommandSize         = Size2.Zero;
                var brokenCommandGlyphOffset  = 0;
                var brokenCommandGlyphLength  = 0;
                var brokenCommandSourceOffset = 0;
                var brokenCommandSourceLength = 0;
                var brokenCommandShapedOffset = 0;
                var brokenCommandShapedLength = 0;

                var newLineHeight = sizeAfterBreak.Height;

                if (newLineHeight == 0)
                {
                    newLineHeight = settings.Font.GetFace(UltravioletFontStyle.Regular).LineSpacing;
                }

                // Truncate the command which is being broken.
                output.Seek(LineBreakCommand.Value);
                unsafe
                {
                    var cmd   = (TextLayoutTextCommand *)output.Data;
                    var shape = (settings.Options & TextLayoutOptions.Shape) == TextLayoutOptions.Shape;

                    brokenCommandGlyphOffset  = cmd->GlyphOffset;
                    brokenCommandGlyphLength  = cmd->GlyphLength;
                    brokenCommandSourceOffset = cmd->SourceOffset;
                    brokenCommandSourceLength = cmd->SourceLength;
                    brokenCommandShapedOffset = cmd->ShapedOffset;
                    brokenCommandShapedLength = cmd->ShapedLength;
                    brokenCommandSize         = cmd->Bounds.Size;

                    cmd->GlyphLength  = LineBreakOffsetOutput.Value;
                    cmd->SourceLength = LineBreakOffsetInput.Value;
                    cmd->ShapedOffset = (shape && settings.Direction == TextDirection.RightToLeft) ? cmd->ShapedOffset + 1 : cmd->ShapedOffset;
                    cmd->ShapedLength = LineBreakOffsetOutput.Value;

                    cmd->TextWidth  = (Int16)sizeBeforeBreak.Width;
                    cmd->TextHeight = (Int16)sizeBeforeBreak.Height;
                }
                output.SeekNextCommand();

                // Insert a line break, a new line, and the second half of the truncated text.
                var part1GlyphLength  = LineBreakOffsetOutput.Value;
                var part1SourceLength = LineBreakOffsetInput.Value;
                var part1ShapedLength = LineBreakOffsetOutput.Value;

                var part2GlyphOffset     = brokenCommandGlyphOffset + (LineBreakOffsetOutput.Value + 1);
                var part2GlyphLength     = brokenCommandGlyphLength - (part1GlyphLength + 1);
                var part2SourceOffset    = brokenCommandSourceOffset + (LineBreakOffsetInput.Value + 1);
                var part2SourceLength    = brokenCommandSourceLength - (part1SourceLength + 1);
                var part2ShapedOffset    = brokenCommandShapedOffset + (LineBreakOffsetOutput.Value + 1);
                var part2ShapedLength    = brokenCommandShapedLength - (part1ShapedLength + 1);
                var part2IsNotDegenerate = (part2GlyphLength > 0);

                var numberOfObjects = part2IsNotDegenerate ? 3 : 2;
                var numberOfBytes   =
                    sizeof(TextLayoutLineBreakCommand) +
                    sizeof(TextLayoutLineInfoCommand) +
                    (part2IsNotDegenerate ? sizeof(TextLayoutTextCommand) : 0);

                var insertionPosition = output.InternalObjectStream.PositionInObjects;

                output.InternalObjectStream.ReserveInsert(numberOfObjects, numberOfBytes);

                *(TextLayoutLineBreakCommand *)output.Data = new TextLayoutLineBreakCommand(part2GlyphOffset - 1, 1, brokenCommandSourceOffset + part1SourceLength, 1);
                output.InternalObjectStream.FinalizeObject(sizeof(TextLayoutLineBreakCommand));

                *(TextLayoutCommandType *)output.Data = TextLayoutCommandType.LineInfo;
                output.InternalObjectStream.FinalizeObject(sizeof(TextLayoutLineInfoCommand));

                if (part2IsNotDegenerate)
                {
                    var glyphOffset = part2GlyphOffset;
                    var glyphLength = part2GlyphLength;

                    var sourceOffset = part2SourceOffset;
                    var sourceLength = part2SourceLength;

                    var shapedOffset = part2ShapedOffset;
                    var shapedLength = part2ShapedLength;

                    *(TextLayoutTextCommand *)output.InternalObjectStream.Data = new TextLayoutTextCommand(
                        glyphOffset, glyphLength, sourceOffset, sourceLength, shapedOffset, shapedLength,
                        0, PositionY + LineHeight, (Int16)sizeAfterBreak.Width, (Int16)sizeAfterBreak.Height);
                    output.InternalObjectStream.FinalizeObject(sizeof(TextLayoutTextCommand));
                }

                // Add the line break command to the broken line.
                AdvanceLineToNextCommand(0, 0, 1, 1, 1, 1);

                // Recalculate the parameters for the broken line.
                output.Seek(LineInfoCommandIndex + 1);

                var brokenLineWidth            = 0;
                var brokenLineHeight           = 0;
                var brokenLineLengthInGlyphs   = 0;
                var brokenLineLengthInSource   = 0;
                var brokenLineLengthInShaped   = 0;
                var brokenLineLengthInCommands = 0;

                var cmdType = TextLayoutCommandType.None;

                while ((cmdType = *(TextLayoutCommandType *)output.Data) != TextLayoutCommandType.LineInfo)
                {
                    switch (cmdType)
                    {
                    case TextLayoutCommandType.Text:
                    {
                        var cmd = (TextLayoutTextCommand *)output.Data;
                        brokenLineWidth          += cmd->TextWidth;
                        brokenLineHeight          = Math.Max(brokenLineHeight, cmd->TextHeight);
                        brokenLineLengthInGlyphs += cmd->GlyphLength;
                        brokenLineLengthInSource += cmd->SourceLength;
                        brokenLineLengthInShaped += cmd->ShapedLength;
                    }
                    break;

                    case TextLayoutCommandType.Icon:
                    {
                        var cmd = (TextLayoutIconCommand *)output.Data;
                        brokenLineWidth          += cmd->Bounds.Width;
                        brokenLineHeight          = Math.Max(brokenLineHeight, cmd->Bounds.Height);
                        brokenLineLengthInGlyphs += 1;
                        brokenLineLengthInSource += cmd->SourceLength;
                    }
                    break;

                    case TextLayoutCommandType.LineBreak:
                    {
                        var cmd = (TextLayoutLineBreakCommand *)output.Data;
                        brokenLineLengthInGlyphs += cmd->GlyphLength;
                        brokenLineLengthInShaped += 1;
                        brokenLineLengthInSource += cmd->SourceLength;
                    }
                    break;
                    }
                    brokenLineLengthInCommands++;
                    output.SeekNextCommand();
                }

                // Finalize the broken line.
                terminatingLineBreakLength = 1;
                TotalSourceLength          = (TotalSourceLength - LineLengthInSource) + brokenLineLengthInSource;
                TotalShapedLength          = (TotalShapedLength - LineLengthInShaped) + brokenLineLengthInShaped;
                TotalGlyphLength           = (TotalGlyphLength - LineLengthInGlyphs) + brokenLineLengthInGlyphs;
                LineWidth            = brokenLineWidth;
                LineHeight           = brokenLineHeight;
                LineLengthInSource   = brokenLineLengthInSource;
                LineLengthInShaped   = brokenLineLengthInShaped;
                LineLengthInGlyphs   = brokenLineLengthInGlyphs;
                LineLengthInCommands = brokenLineLengthInCommands;
                FinalizeLine(output, ref settings);

                // Fixup token bounds and update parameters for new line.
                LineInfoCommandIndex = insertionPosition + 1;
                while (output.StreamPositionInObjects < output.Count)
                {
                    var width            = 0;
                    var height           = 0;
                    var lengthInCommands = 0;
                    var lengthInGlyphs   = 0;
                    var lengthInSource   = 0;
                    var lengthInShaped   = 0;

                    switch (*(TextLayoutCommandType *)output.Data)
                    {
                    case TextLayoutCommandType.Text:
                    {
                        var cmd = (TextLayoutTextCommand *)output.Data;
                        width            = cmd->TextWidth;
                        height           = cmd->TextHeight;
                        lengthInCommands = 1;
                        lengthInGlyphs   = cmd->GlyphLength;
                        lengthInSource   = cmd->SourceLength;
                        lengthInShaped   = cmd->ShapedLength;
                        cmd->TextX       = PositionX;
                        cmd->TextY       = PositionY;
                    }
                    break;

                    case TextLayoutCommandType.Icon:
                    {
                        var cmd = (TextLayoutIconCommand *)output.Data;
                        width            = cmd->IconWidth;
                        height           = cmd->IconHeight;
                        lengthInCommands = 1;
                        lengthInGlyphs   = 1;
                        lengthInSource   = cmd->SourceLength;
                        lengthInShaped   = 0;
                        cmd->IconX       = PositionX;
                        cmd->IconY       = PositionY;
                    }
                    break;

                    case TextLayoutCommandType.LineBreak:
                    {
                        var cmd = (TextLayoutLineBreakCommand *)output.Data;
                        lengthInGlyphs = cmd->GlyphLength;
                        lengthInSource = cmd->SourceLength;
                        lengthInShaped = 0;
                    }
                    break;
                    }

                    AdvanceLineToNextCommand(width, height, lengthInCommands, lengthInGlyphs, lengthInSource, lengthInShaped);
                    output.SeekNextCommand();
                }

                return(true);
            }