/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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++; }
/// <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); }
/// <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; }
/// <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; }
/// <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++; }
/// <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); }