示例#1
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PopLink"/>.
 /// </summary>
 private void ProcessPopLinkToken(TextLayoutCommandStream output,
                                  ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     output.WritePopLink();
     state.AdvanceLineToNextCommand();
     index++;
 }
示例#2
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.ToggleBold"/>.
 /// </summary>
 private void ProcessToggleBoldToken(TextLayoutCommandStream output, ref Boolean bold,
                                     ref LayoutState state, ref Int32 index)
 {
     output.WriteToggleBold();
     state.AdvanceLineToNextCommand();
     bold = !bold;
     index++;
 }
示例#3
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.ToggleItalic"/>.
 /// </summary>
 private void ProcessToggleItalicToken(TextLayoutCommandStream output, ref Boolean italic,
                                       ref LayoutState state, ref Int32 index)
 {
     output.WriteToggleItalic();
     state.AdvanceLineToNextCommand();
     italic = !italic;
     index++;
 }
示例#4
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PopStyle"/>.
 /// </summary>
 private void ProcessPopStyleToken(TextLayoutCommandStream output, ref Boolean bold, ref Boolean italic,
                                   ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     output.WritePopStyle();
     state.AdvanceLineToNextCommand();
     PopStyle(ref bold, ref italic);
     index++;
 }
示例#5
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.PushColor"/>.
        /// </summary>
        private void ProcessPushColorToken(TextLayoutCommandStream output,
                                           ref TextParserToken token, ref LayoutState state, ref Int32 index)
        {
            var pushedColor = ParseColor(token.Text);

            output.WritePushColor(new TextLayoutColorCommand(pushedColor));
            state.AdvanceLineToNextCommand();
            index++;
        }
示例#6
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.PushLink"/>.
        /// </summary>
        private void ProcessPushLinkToken(TextLayoutCommandStream output,
                                          ref TextParserToken token, ref LayoutState state, ref Int32 index)
        {
            var pushedLinkTargetIndex = RegisterLinkTargetWithCommandStream(output, token.Text);

            output.WritePushLink(new TextLayoutLinkCommand(pushedLinkTargetIndex));
            state.AdvanceLineToNextCommand();
            index++;
        }
示例#7
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.PushGlyphShader"/>.
        /// </summary>
        private void ProcessPushGlyphShaderToken(TextLayoutCommandStream output,
                                                 ref TextParserToken token, ref LayoutState state, ref Int32 index)
        {
            var pushedGlyphShader      = default(GlyphShader);
            var pushedGlyphShaderIndex = RegisterGlyphShaderWithCommandStream(output, token.Text, out pushedGlyphShader);

            output.WritePushGlyphShader(new TextLayoutGlyphShaderCommand(pushedGlyphShaderIndex));
            state.AdvanceLineToNextCommand();
            index++;
        }
示例#8
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.Custom"/>.
        /// </summary>
        private void ProcessCustomCommandToken(TextLayoutCommandStream output,
                                               ref TextParserToken token, ref LayoutState state, ref Int32 index)
        {
            var commandID    = (token.TokenType - TextParserTokenType.Custom);
            var commandValue = token.Text.IsEmpty ? default(Int32) : StringSegmentConversion.ParseInt32(token.Text);

            output.WriteCustomCommand(new TextLayoutCustomCommand(commandID, commandValue));
            state.AdvanceLineToNextCommand();
            index++;
        }
示例#9
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.PushFont"/>.
        /// </summary>
        private void ProcessPushFontToken(TextLayoutCommandStream output,
                                          ref TextParserToken token, ref LayoutState state, ref Int32 index)
        {
            var pushedFont      = default(SpriteFont);
            var pushedFontIndex = RegisterFontWithCommandStream(output, token.Text, out pushedFont);

            output.WritePushFont(new TextLayoutFontCommand(pushedFontIndex));
            state.AdvanceLineToNextCommand();
            PushFont(pushedFont);
            index++;
        }
示例#10
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.PushStyle"/>.
        /// </summary>
        private void ProcessPushStyleToken(TextLayoutCommandStream output, ref Boolean bold, ref Boolean italic,
                                           ref TextParserToken token, ref LayoutState state, ref Int32 index)
        {
            var pushedStyle      = default(TextStyle);
            var pushedStyleIndex = RegisterStyleWithCommandStream(output, token.Text, out pushedStyle);

            output.WritePushStyle(new TextLayoutStyleCommand(pushedStyleIndex));
            state.AdvanceLineToNextCommand();
            PushStyle(pushedStyle, ref bold, ref italic);
            index++;
        }
示例#11
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.Icon"/>.
        /// </summary>
        private Boolean ProcessIconToken(TextLayoutCommandStream output,
                                         ref TextParserToken token, ref LayoutState state, ref TextLayoutSettings settings, ref Int32 index)
        {
            var icon      = default(TextIconInfo);
            var iconIndex = RegisterIconWithCommandStream(output, token.Text, out icon);
            var iconSize  = MeasureToken(null, token.TokenType, token.Text);

            if (state.PositionX + iconSize.Width > (settings.Width ?? Int32.MaxValue))
            {
                state.AdvanceLayoutToNextLine(output, ref settings);
            }

            if (state.PositionY + iconSize.Height > (settings.Height ?? Int32.MaxValue))
            {
                return(false);
            }

            output.WriteIcon(new TextLayoutIconCommand(iconIndex, state.PositionX, state.PositionY, (Int16)iconSize.Width, (Int16)iconSize.Height));
            state.AdvanceLineToNextCommand(iconSize.Width, iconSize.Height, 1, 1);
            index++;

            return(true);
        }
示例#12
0
        /// <summary>
        /// Processes a parser token with type <see cref="TextParserTokenType.Icon"/>.
        /// </summary>
        private Boolean ProcessIconToken(TextLayoutCommandStream output,
            ref TextParserToken token, ref LayoutState state, ref TextLayoutSettings settings, ref Int32 index)
        {
            var icon = default(TextIconInfo);
            var iconIndex = RegisterIconWithCommandStream(output, token.Text, out icon);
            var iconSize = MeasureToken(null, token.TokenType, token.Text);

            if (state.PositionX + iconSize.Width > (settings.Width ?? Int32.MaxValue))
                state.AdvanceLayoutToNextLine(output, ref settings);

            if (state.PositionY + iconSize.Height > (settings.Height ?? Int32.MaxValue))
                return false;

            output.WriteIcon(new TextLayoutIconCommand(iconIndex, state.PositionX, state.PositionY, (Int16)iconSize.Width, (Int16)iconSize.Height));
            state.AdvanceLineToNextCommand(iconSize.Width, iconSize.Height, 1, 1);
            index++;

            return true;
        }
示例#13
0
        /// <summary>
        /// Accumulates sequential text tokens into a single text command.
        /// </summary>
        private Boolean AccumulateText(TextParserTokenStream input, TextLayoutCommandStream output, SpriteFontFace font, ref Int32 index, ref LayoutState state, ref TextLayoutSettings settings)
        {
            var hyphenate = (settings.Options & TextLayoutOptions.Hyphenate) == TextLayoutOptions.Hyphenate;

            var availableWidth = (settings.Width ?? Int32.MaxValue);
            var x = state.PositionX;
            var y = state.PositionY;
            var width = 0;
            var height = 0;

            var accumulatedStart = input[index].Text.Start + (state.ParserTokenOffset ?? 0);
            var accumulatedLength = 0;
            var accumulatedCount = 0;

            var lineOverflow = false;
            var lineBreakPossible = false;

            var tokenText = default(StringSegment);
            var tokenNext = default(TextParserToken?);
            var tokenSize = default(Size2);

            while (index < input.Count)
            {
                var token = input[index];
                if (token.TokenType != TextParserTokenType.Text || token.IsNewLine)
                    break;

                if (!IsSegmentForCurrentSource(token.Text))
                {
                    if (accumulatedCount > 0)
                        break;

                    EmitChangeSourceIfNecessary(input, output, ref token);
                }

                tokenText = token.Text.Substring(state.ParserTokenOffset ?? 0);
                tokenNext = GetNextTextToken(input, index);
                tokenSize = MeasureToken(font, token.TokenType, tokenText, tokenNext);

                // NOTE: We assume in a couple of places that tokens sizes don't exceed Int16.MaxValue, so try to
                // avoid accumulating tokens larger than that just in case somebody is doing something dumb
                if (width + tokenSize.Width > Int16.MaxValue)
                    break;

                var overflowsLine = state.PositionX + tokenSize.Width > availableWidth;
                if (overflowsLine)
                {
                    lineOverflow = true;
                    break;
                }

                if (tokenText.Start != accumulatedStart + accumulatedLength)
                    break;

                if (token.IsWhiteSpace && (state.LineBreakCommand == null || !token.IsNonBreakingSpace))
                {
                    lineBreakPossible = true;
                    state.LineBreakCommand = output.Count;
                    state.LineBreakOffset = accumulatedLength + token.Text.Length - 1;
                }

                width = width + tokenSize.Width;
                height = Math.Max(height, tokenSize.Height);
                accumulatedLength = accumulatedLength + tokenText.Length;
                accumulatedCount++;

                state.AdvanceLineToNextCommand(tokenSize.Width, tokenSize.Height, 1, tokenText.Length);
                state.ParserTokenOffset = 0;
                state.LineLengthInCommands--;

                index++;
            }

            if (lineBreakPossible)
            {
                var preLineBreakTextStart = accumulatedStart;
                var preLineBreakTextLength = state.LineBreakOffset.Value;
                var preLineBreakText = CreateStringSegmentFromCurrentSource(preLineBreakTextStart, preLineBreakTextLength);
                var preLineBreakSize = (preLineBreakText.Length == 0) ? Size2.Zero :
                    MeasureToken(font, TextParserTokenType.Text, preLineBreakText);
                state.BrokenTextSizeBeforeBreak = preLineBreakSize;

                var postLineBreakStart = accumulatedStart + (state.LineBreakOffset.Value + 1);
                var postLineBreakLength = accumulatedLength - (state.LineBreakOffset.Value + 1);
                var postLineBreakText = CreateStringSegmentFromCurrentSource(postLineBreakStart, postLineBreakLength);
                var postLineBreakSize = (postLineBreakText.Length == 0) ? Size2.Zero :
                    MeasureToken(font, TextParserTokenType.Text, postLineBreakText, GetNextTextToken(input, index - 1));
                state.BrokenTextSizeAfterBreak = postLineBreakSize;
            }

            var bounds = new Rectangle(x, y, width, height);
            EmitTextIfNecessary(output, accumulatedStart, accumulatedLength, ref bounds, ref state);

            if (lineOverflow && !state.ReplaceLastBreakingSpaceWithLineBreak(output, ref settings))
            {
                var overflowingToken = input[index];
                if (overflowingToken.IsWhiteSpace && !overflowingToken.IsNonBreakingSpace)
                {
                    output.WriteLineBreak(new TextLayoutLineBreakCommand(1));
                    state.AdvanceLineToNextCommand(0, 0, 1, 1, isLineBreak: true);
                    state.AdvanceLayoutToNextLine(output, ref settings);

                    if (overflowingToken.Text.Length > 1)
                    {
                        state.ParserTokenOffset = 1;
                    }
                    else
                    {
                        index++;
                    }
                    return true;
                }

                if (!GetFittedSubstring(font, availableWidth, ref tokenText, ref tokenSize, ref state, hyphenate) && state.LineWidth == 0)
                    return false;

                var overflowingTokenBounds = (tokenText.Length == 0) ? Rectangle.Empty :
                    new Rectangle(state.PositionX, state.PositionY, tokenSize.Width, tokenSize.Height);

                var overflowingTextEmitted = EmitTextIfNecessary(output, tokenText.Start, tokenText.Length, ref overflowingTokenBounds, ref state);
                if (overflowingTextEmitted)
                {
                    state.AdvanceLineToNextCommand(tokenSize.Width, tokenSize.Height, 0, tokenText.Length);
                    if (hyphenate)
                    {
                        output.WriteHyphen();
                        state.AdvanceLineToNextCommand(0, 0, 1, 0);
                    }
                }

                state.ParserTokenOffset = (state.ParserTokenOffset ?? 0) + tokenText.Length;
                state.AdvanceLayoutToNextLine(output, ref settings);
            }

            return true;
        }
示例#14
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.ToggleItalic"/>.
 /// </summary>
 private void ProcessToggleItalicToken(TextLayoutCommandStream output, ref Boolean italic,
     ref LayoutState state, ref Int32 index)
 {
     output.WriteToggleItalic();
     state.AdvanceLineToNextCommand();
     italic = !italic;
     index++;
 }
示例#15
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.ToggleBold"/>.
 /// </summary>
 private void ProcessToggleBoldToken(TextLayoutCommandStream output, ref Boolean bold,
     ref LayoutState state, ref Int32 index)
 {
     output.WriteToggleBold();
     state.AdvanceLineToNextCommand();
     bold = !bold;
     index++;
 }
示例#16
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PushColor"/>.
 /// </summary>
 private void ProcessPushColorToken(TextLayoutCommandStream output,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     var pushedColor = ParseColor(token.Text);
     output.WritePushColor(new TextLayoutColorCommand(pushedColor));
     state.AdvanceLineToNextCommand();
     index++;
 }
示例#17
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PushFont"/>.
 /// </summary>
 private void ProcessPushFontToken(TextLayoutCommandStream output,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     var pushedFont = default(SpriteFont);
     var pushedFontIndex = RegisterFontWithCommandStream(output, token.Text, out pushedFont);
     output.WritePushFont(new TextLayoutFontCommand(pushedFontIndex));
     state.AdvanceLineToNextCommand();
     PushFont(pushedFont);
     index++;
 }
示例#18
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PushStyle"/>.
 /// </summary>
 private void ProcessPushStyleToken(TextLayoutCommandStream output, ref Boolean bold, ref Boolean italic,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     var pushedStyle = default(TextStyle);
     var pushedStyleIndex = RegisterStyleWithCommandStream(output, token.Text, out pushedStyle);
     output.WritePushStyle(new TextLayoutStyleCommand(pushedStyleIndex));
     state.AdvanceLineToNextCommand();
     PushStyle(pushedStyle, ref bold, ref italic);
     index++;
 }
示例#19
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PushGlyphShader"/>.
 /// </summary>
 private void ProcessPushGlyphShaderToken(TextLayoutCommandStream output,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     var pushedGlyphShader = default(GlyphShader);
     var pushedGlyphShaderIndex = RegisterGlyphShaderWithCommandStream(output, token.Text, out pushedGlyphShader);
     output.WritePushGlyphShader(new TextLayoutGlyphShaderCommand(pushedGlyphShaderIndex));
     state.AdvanceLineToNextCommand();
     index++;
 }
示例#20
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PopStyle"/>.
 /// </summary>
 private void ProcessPopStyleToken(TextLayoutCommandStream output, ref Boolean bold, ref Boolean italic,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     output.WritePopStyle();
     state.AdvanceLineToNextCommand();
     PopStyle(ref bold, ref italic);
     index++;
 }
示例#21
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PushLink"/>.
 /// </summary>
 private void ProcessPushLinkToken(TextLayoutCommandStream output,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     var pushedLinkTargetIndex = RegisterLinkTargetWithCommandStream(output, token.Text);
     output.WritePushLink(new TextLayoutLinkCommand(pushedLinkTargetIndex));
     state.AdvanceLineToNextCommand();
     index++;
 }
示例#22
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.PopGlyphShader"/>.
 /// </summary>
 private void ProcessPopGlyphShaderToken(TextLayoutCommandStream output,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     output.WritePopGlyphShader();
     state.AdvanceLineToNextCommand();
     index++;
 }
示例#23
0
        /// <summary>
        /// Accumulates sequential text tokens into a single text command.
        /// </summary>
        private Boolean AccumulateText(TextParserTokenStream input, TextLayoutCommandStream output, SpriteFontFace font, ref Int32 index, ref LayoutState state, ref TextLayoutSettings settings)
        {
            var hyphenate = (settings.Options & TextLayoutOptions.Hyphenate) == TextLayoutOptions.Hyphenate;

            var availableWidth = (settings.Width ?? Int32.MaxValue);
            var x      = state.PositionX;
            var y      = state.PositionY;
            var width  = 0;
            var height = 0;

            var accumulatedStart  = input[index].Text.Start + (state.ParserTokenOffset ?? 0);
            var accumulatedLength = 0;
            var accumulatedCount  = 0;

            var lineOverflow      = false;
            var lineBreakPossible = false;

            var tokenText            = default(StringSegment);
            var tokenNext            = default(TextParserToken?);
            var tokenSize            = default(Size2);
            var tokenKerning         = 0;
            var tokenIsBreakingSpace = false;

            while (index < input.Count)
            {
                var token = input[index];
                if (token.TokenType != TextParserTokenType.Text || token.IsNewLine)
                {
                    break;
                }

                if (!IsSegmentForCurrentSource(token.Text))
                {
                    if (accumulatedCount > 0)
                    {
                        break;
                    }

                    EmitChangeSourceIfNecessary(input, output, ref token);
                }

                tokenText    = token.Text.Substring(state.ParserTokenOffset ?? 0);
                tokenNext    = GetNextTextToken(input, index);
                tokenSize    = MeasureToken(font, token.TokenType, tokenText, tokenNext);
                tokenKerning = font.Kerning.Get(tokenText[tokenText.Length - 1], ' ');

                // NOTE: We assume in a couple of places that tokens sizes don't exceed Int16.MaxValue, so try to
                // avoid accumulating tokens larger than that just in case somebody is doing something dumb
                if (width + tokenSize.Width > Int16.MaxValue)
                {
                    break;
                }

                if (token.IsWhiteSpace && (state.LineBreakCommand == null || !token.IsNonBreakingSpace))
                {
                    lineBreakPossible      = true;
                    state.LineBreakCommand = output.Count;
                    state.LineBreakOffset  = accumulatedLength + token.Text.Length - 1;
                    tokenIsBreakingSpace   = true;
                }
                else
                {
                    tokenIsBreakingSpace = false;
                }

                // For most tokens we need to bail out here if there's a line overflow, but
                // if it's a breaking space we need to be sure that it's part of the command stream
                // so that we can go back and replace it in the line break phase!
                var overflowsLine = state.PositionX + tokenSize.Width - tokenKerning > availableWidth;
                if (overflowsLine && !tokenIsBreakingSpace)
                {
                    lineOverflow = true;
                    break;
                }

                if (tokenText.Start != accumulatedStart + accumulatedLength)
                {
                    break;
                }

                width             = width + tokenSize.Width;
                height            = Math.Max(height, tokenSize.Height);
                accumulatedLength = accumulatedLength + tokenText.Length;
                accumulatedCount++;

                state.AdvanceLineToNextCommand(tokenSize.Width, tokenSize.Height, 1, tokenText.Length);
                state.ParserTokenOffset = 0;
                state.LineLengthInCommands--;

                index++;

                // At this point, we need to bail out even for breaking spaces.
                if (overflowsLine && tokenIsBreakingSpace)
                {
                    lineOverflow = true;
                    break;
                }
            }

            if (lineBreakPossible)
            {
                var preLineBreakTextStart  = accumulatedStart;
                var preLineBreakTextLength = state.LineBreakOffset.Value;
                var preLineBreakText       = CreateStringSegmentFromCurrentSource(preLineBreakTextStart, preLineBreakTextLength);
                var preLineBreakSize       = (preLineBreakText.Length == 0) ? Size2.Zero :
                                             MeasureToken(font, TextParserTokenType.Text, preLineBreakText);
                state.BrokenTextSizeBeforeBreak = preLineBreakSize;

                var postLineBreakStart  = accumulatedStart + (state.LineBreakOffset.Value + 1);
                var postLineBreakLength = accumulatedLength - (state.LineBreakOffset.Value + 1);
                var postLineBreakText   = CreateStringSegmentFromCurrentSource(postLineBreakStart, postLineBreakLength);
                var postLineBreakSize   = (postLineBreakText.Length == 0) ? Size2.Zero :
                                          MeasureToken(font, TextParserTokenType.Text, postLineBreakText, GetNextTextToken(input, index - 1));
                state.BrokenTextSizeAfterBreak = postLineBreakSize;
            }

            var bounds = new Rectangle(x, y, width, height);

            EmitTextIfNecessary(output, accumulatedStart, accumulatedLength, ref bounds, ref state);

            if (lineOverflow && !state.ReplaceLastBreakingSpaceWithLineBreak(output, ref settings))
            {
                var overflowingToken = input[index];
                if (overflowingToken.IsWhiteSpace && !overflowingToken.IsNonBreakingSpace)
                {
                    output.WriteLineBreak(new TextLayoutLineBreakCommand(1));
                    state.AdvanceLineToNextCommand(0, 0, 1, 1, isLineBreak: true);
                    state.AdvanceLayoutToNextLine(output, ref settings);

                    if (overflowingToken.Text.Length > 1)
                    {
                        state.ParserTokenOffset = 1;
                    }
                    else
                    {
                        index++;
                    }
                    return(true);
                }

                if (!GetFittedSubstring(font, availableWidth, ref tokenText, ref tokenSize, ref state, hyphenate) && state.LineWidth == 0)
                {
                    return(false);
                }

                var overflowingTokenBounds = (tokenText.Length == 0) ? Rectangle.Empty :
                                             new Rectangle(state.PositionX, state.PositionY, tokenSize.Width, tokenSize.Height);

                var overflowingTextEmitted = EmitTextIfNecessary(output, tokenText.Start, tokenText.Length, ref overflowingTokenBounds, ref state);
                if (overflowingTextEmitted)
                {
                    state.AdvanceLineToNextCommand(tokenSize.Width, tokenSize.Height, 0, tokenText.Length);
                    if (hyphenate)
                    {
                        output.WriteHyphen();
                        state.AdvanceLineToNextCommand(0, 0, 1, 0);
                    }
                }

                state.ParserTokenOffset = (state.ParserTokenOffset ?? 0) + tokenText.Length;
                state.AdvanceLayoutToNextLine(output, ref settings);
            }

            return(true);
        }
示例#24
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.Custom"/>.
 /// </summary>
 private void ProcessCustomCommandToken(TextLayoutCommandStream output,
     ref TextParserToken token, ref LayoutState state, ref Int32 index)
 {
     var commandID = (token.TokenType - TextParserTokenType.Custom);
     var commandValue = token.Text.IsEmpty ? default(Int32) : StringSegmentConversion.ParseInt32(token.Text);
     output.WriteCustomCommand(new TextLayoutCustomCommand(commandID, commandValue));
     state.AdvanceLineToNextCommand();
     index++;
 }