Ejemplo n.º 1
0
        /// <summary>
        /// Calculates the size of the specified parser token when rendered according to the current layout state.
        /// </summary>
        /// <param name="font">The current font face.</param>
        /// <param name="tokenType">The type of the current token.</param>
        /// <param name="tokenText">The text of the current token.</param>
        /// <param name="tokenNext">The next token after the current token, excluding command tokens.</param>
        /// <returns>The size of the specified token in pixels.</returns>
        private Size2 MeasureToken(SpriteFontFace font, TextParserTokenType tokenType, StringSegment tokenText, TextParserToken?tokenNext = null)
        {
            switch (tokenType)
            {
            case TextParserTokenType.Icon:
                {
                    TextIconInfo icon;
                    if (!registeredIcons.TryGetValue(tokenText, out icon))
                    {
                        throw new InvalidOperationException(UltravioletStrings.UnrecognizedIcon.Format(tokenText));
                    }

                    return(new Size2(icon.Width ?? icon.Icon.Controller.Width, icon.Height ?? icon.Icon.Controller.Height));
                }

            case TextParserTokenType.Text:
            {
                var size = font.MeasureString(tokenText);
                if (tokenNext.HasValue)
                {
                    var tokenNextValue = tokenNext.GetValueOrDefault();
                    if (tokenNextValue.TokenType == TextParserTokenType.Text && !tokenNextValue.Text.IsEmpty && !tokenNextValue.IsNewLine)
                    {
                        var charLast = tokenText[tokenText.Length - 1];
                        var charNext = tokenNextValue.Text[0];
                        var kerning  = font.Kerning.Get(charLast, charNext);
                        return(new Size2(size.Width + kerning, size.Height));
                    }
                }
                return(size);
            }
            }
            return(Size2.Zero);
        }
        /// <inheritdoc/>
        public override SpriteFont ImportPreprocessed(ContentManager manager, IContentProcessorMetadata metadata, BinaryReader reader)
        {
            var imgDataExtension = reader.ReadString();
            var imgDataLength    = reader.ReadInt32();
            var imgData          = reader.ReadBytes(imgDataLength);

            Texture2D texture;

            using (var stream = new MemoryStream(imgData))
                texture = manager.LoadFromStream <Texture2D>(stream, imgDataExtension);

            var glyphCount = reader.ReadInt32();
            var glyphSubst = reader.ReadChar();

            var glyphPositions = new Rectangle[glyphCount];

            for (int i = 0; i < glyphCount; i++)
            {
                var glyphX      = reader.ReadInt32();
                var glyphY      = reader.ReadInt32();
                var glyphWidth  = reader.ReadInt32();
                var glyphHeight = reader.ReadInt32();
                glyphPositions[i] = new Rectangle(glyphX, glyphY, glyphWidth, glyphHeight);
            }

            var fontFace = new SpriteFontFace(manager.Ultraviolet, texture, null, glyphPositions, glyphSubst, true);
            var font     = new SpriteFont(manager.Ultraviolet, fontFace);

            return(font);
        }
        /// <summary>
        /// Imports a font face from the specified preprocessed asset stream.
        /// </summary>
        private static SpriteFontFace ImportPreprocessedFace(ContentManager manager,
                                                             IContentProcessorMetadata metadata, BinaryReader reader, IEnumerable <CharacterRegion> characterRegions)
        {
            var faceExists = reader.ReadBoolean();

            if (!faceExists)
            {
                return(null);
            }

            var texturePath            = reader.ReadString();
            var texture                = manager.Load <Texture2D>(texturePath);
            var textureRegionSpecified = reader.ReadBoolean();

            if (textureRegionSpecified)
            {
                reader.ReadInt32();
                reader.ReadInt32();
                reader.ReadInt32();
                reader.ReadInt32();
            }

            var substitution = reader.ReadChar();

            var glyphPositions = new List <Rectangle>();
            var glyphCount     = reader.ReadInt32();

            for (int j = 0; j < glyphCount; j++)
            {
                var glyphX      = reader.ReadInt32();
                var glyphY      = reader.ReadInt32();
                var glyphWidth  = reader.ReadInt32();
                var glyphHeight = reader.ReadInt32();

                glyphPositions.Add(new Rectangle(glyphX, glyphY, glyphWidth, glyphHeight));
            }

            var face    = new SpriteFontFace(manager.Ultraviolet, texture, characterRegions, glyphPositions, substitution);
            var kerning = new Dictionary <SpriteFontKerningPair, Int32>();
            var kerningDefaultAdjustment = reader.ReadInt32();
            var kerningCount             = reader.ReadInt32();

            for (int j = 0; j < kerningCount; j++)
            {
                var pairFirstChar  = reader.ReadChar();
                var pairSecondChar = reader.ReadChar();
                var offset         = reader.ReadInt32();

                kerning[new SpriteFontKerningPair(pairFirstChar, pairSecondChar)] = offset;
            }

            face.Kerning.DefaultAdjustment = kerningDefaultAdjustment;
            foreach (var kvp in kerning)
            {
                face.Kerning.Set(kvp.Key, kvp.Value);
            }

            return(face);
        }
        /// <inheritdoc/>
        public override SpriteFont Process(ContentManager manager, IContentProcessorMetadata metadata, SDL_Surface input)
        {
            var positions = OpenGLSpriteFontHelper.IdentifyGlyphs(input);
            var texture   = manager.Process <SDL_Surface, Texture2D>(input);
            var face      = new SpriteFontFace(manager.Ultraviolet, texture, null, positions, true);

            return(new SpriteFont(manager.Ultraviolet, face));
        }
Ejemplo n.º 5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TextLayoutToken"/> structure.
 /// </summary>
 /// <param name="text">The token's text.</param>
 /// <param name="bounds">The token's bounds relative to its layout region.</param>
 /// <param name="fontFace">The token's font face.</param>
 /// <param name="icon">The token's icon.</param>
 /// <param name="glyphShader">The token's glyph shader.</param>
 /// <param name="color">The token's color.</param>
 internal TextLayoutToken(StringSegment text, Rectangle bounds, SpriteFontFace fontFace, TextIconInfo? icon, GlyphShader glyphShader, Color? color)
 {
     this.text = text;
     this.bounds = bounds;
     this.fontFace = fontFace;
     this.icon = icon;
     this.glyphShader = glyphShader;
     this.color = color;
 }
Ejemplo n.º 6
0
        /// <summary>
        /// Draws a string of text.
        /// </summary>
        /// <param name="fontFace">The <see cref="SpriteFontFace"/> with which to draw the text.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="position">The text's position.</param>
        /// <param name="color">The text's color.</param>
        /// <param name="rotation">The text's rotation in radians.</param>
        /// <param name="origin">The text's point of origin relative to its top-left corner.</param>
        /// <param name="scale">The text's scale factor.</param>
        /// <param name="effects">The text's rendering effects.</param>
        /// <param name="layerDepth">The text's layer depth.</param>
        public void DrawString(SpriteFontFace fontFace, StringSegment text, Vector2 position, Color color, Single rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, Single layerDepth)
        {
            if (SpriteBatch == null)
            {
                throw new InvalidOperationException(PresentationStrings.DrawingContextDoesNotHaveSpriteBatch);
            }

            SpriteBatch.DrawString(fontFace, text, position, color * Opacity, rotation, origin, scale, effects, layerDepth);
        }
Ejemplo n.º 7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TextLayoutToken"/> structure.
 /// </summary>
 /// <param name="text">The token's text.</param>
 /// <param name="bounds">The token's bounds relative to its layout region.</param>
 /// <param name="fontFace">The token's font face.</param>
 /// <param name="icon">The token's icon.</param>
 /// <param name="glyphShader">The token's glyph shader.</param>
 /// <param name="color">The token's color.</param>
 internal TextLayoutToken(StringSegment text, Rectangle bounds, SpriteFontFace fontFace, TextIconInfo?icon, GlyphShader glyphShader, Color?color)
 {
     this.text        = text;
     this.bounds      = bounds;
     this.fontFace    = fontFace;
     this.icon        = icon;
     this.glyphShader = glyphShader;
     this.color       = color;
 }
Ejemplo n.º 8
0
        /// <summary>
        /// Draws a string of text.
        /// </summary>
        /// <param name="fontFace">The <see cref="SpriteFontFace"/> with which to draw the text.</param>
        /// <param name="text">The text to draw.</param>
        /// <param name="position">The text's position.</param>
        /// <param name="color">The text's color.</param>
        public void DrawString(SpriteFontFace fontFace, StringSegment text, Vector2 position, Color color)
        {
            if (SpriteBatch == null)
            {
                throw new InvalidOperationException(PresentationStrings.DrawingContextDoesNotHaveSpriteBatch);
            }

            SpriteBatch.DrawString(fontFace, text, position, color * Opacity);
        }
        /// <summary>
        /// Processes the definition for a single font face.
        /// </summary>
        private static SpriteFontFace ProcessFace(Dictionary <String, PlatformNativeSurface> textures, ContentManager manager,
                                                  IContentProcessorMetadata metadata, SpriteFontFaceDescription description, String style, IEnumerable <CharacterRegion> characterRegions)
        {
            if (description == null)
            {
                return(null);
            }

            var textureName   = ResolveDependencyAssetPath(metadata, description.Texture);
            var textureRegion = description.TextureRegion;

            if (String.IsNullOrEmpty(textureName))
            {
                throw new ContentLoadException(OpenGLStrings.InvalidSpriteFontTexture);
            }

            var faceSurface = textures[textureName];
            var faceGlyphs  = OpenGLSpriteFontHelper.IdentifyGlyphs(faceSurface, textureRegion);

            var kerningDefaultAdjustment = description.Kernings?["default"] ?? 0;
            var kerningPairs             = description.Kernings?.Where(x => !String.Equals(x.Key, "default", StringComparison.InvariantCulture))
                                           .ToDictionary(x => CreateKerningPair(x.Key), x => x.Value);

            var kerning = new SpriteFontKerning()
            {
                DefaultAdjustment = kerningDefaultAdjustment
            };

            foreach (var kvp in kerningPairs)
            {
                kerning.Set(kvp.Key, kvp.Value);
            }

            var ascender  = description.Ascender;
            var descender = description.Descender;

            var faceTexture = manager.Load <Texture2D>(textureName, metadata.AssetDensity);
            var face        = new SpriteFontFace(manager.Ultraviolet,
                                                 faceTexture, characterRegions, faceGlyphs, kerning, ascender, descender, description.Glyphs?.Substitution ?? '?');

            return(face);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Given a string and an available space, returns the largest substring which will fit within that space.
        /// </summary>
        private Boolean GetFittedSubstring(SpriteFontFace font, Int32 maxLineWidth, ref StringSegment tokenText, ref Size2 tokenSize, ref LayoutState state, Boolean hyphenate)
        {
            var substringAvailableWidth = maxLineWidth - state.PositionX;
            var substringWidth          = 0;
            var substringLength         = 0;

            for (int glyphIndex = 0; glyphIndex < tokenText.Length - 1; glyphIndex++)
            {
                var glyph1     = tokenText[glyphIndex];
                var glyph2     = tokenText[glyphIndex + 1];
                var glyphWidth = 0;

                if (hyphenate)
                {
                    glyphWidth = font.MeasureGlyph(glyph1, '-').Width + font.MeasureGlyph('-').Width;
                }
                else
                {
                    glyphWidth = font.MeasureGlyph(glyph1).Width;
                }

                if (substringAvailableWidth - glyphWidth < 0)
                {
                    break;
                }

                var glyphSize = font.MeasureGlyph(glyph1, glyph2);
                substringAvailableWidth -= glyphSize.Width;
                substringWidth          += glyphSize.Width;
                substringLength++;
            }

            tokenText = substringLength > 0 ? tokenText.Substring(0, substringLength) : StringSegment.Empty;
            tokenSize = new Size2(substringWidth, tokenSize.Height);

            return(substringLength > 0);
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.Text"/>.
 /// </summary>
 private Boolean ProcessTextToken(TextParserTokenStream input, TextLayoutCommandStream output, SpriteFontFace currentFontFace,
     ref TextParserToken token, ref LayoutState state, ref TextLayoutSettings settings, ref Int32 index)
 {
     if (token.IsNewLine)
     {
         state.AdvanceLayoutToNextLineWithBreak(output, token.SourceLength, ref settings);
         index++;
     }
     else
     {
         if (!AccumulateText(input, output, currentFontFace, ref index, ref state, ref settings))
             return false;
     }
     return true;
 }
Ejemplo n.º 12
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);
        }
Ejemplo n.º 13
0
 /// <summary>
 /// Processes a parser token with type <see cref="TextParserTokenType.Text"/>.
 /// </summary>
 private Boolean ProcessTextToken(TextParserTokenStream input, TextLayoutCommandStream output, SpriteFontFace currentFontFace,
                                  ref TextParserToken token, ref LayoutState state, ref TextLayoutSettings settings, ref Int32 index)
 {
     if (token.IsNewLine)
     {
         state.AdvanceLayoutToNextLineWithBreak(output, token.SourceLength, ref settings);
         index++;
     }
     else
     {
         if (!AccumulateText(input, output, currentFontFace, ref index, ref state, ref settings))
         {
             return(false);
         }
     }
     return(true);
 }
Ejemplo n.º 14
0
        /// <summary>
        /// Gets the currently active font.
        /// </summary>
        private SpriteFont GetCurrentFont(ref TextLayoutSettings settings, Boolean bold, Boolean italic, out SpriteFontFace face)
        {
            var font = (fontStack.Count == 0) ? settings.Font : fontStack.Peek().Value;

            face = font.GetFace(bold, italic);
            return(font);
        }
Ejemplo n.º 15
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;
        }
Ejemplo n.º 16
0
 /// <summary>
 /// Gets the currently active font.
 /// </summary>
 private SpriteFont GetCurrentFont(ref TextLayoutSettings settings, Boolean bold, Boolean italic, out SpriteFontFace face)
 {
     var font = (fontStack.Count == 0) ? settings.Font : fontStack.Peek().Value;
     face = font.GetFace(bold, italic);
     return font;
 }
Ejemplo n.º 17
0
        /// <summary>
        /// Calculates the size of the specified parser token when rendered according to the current layout state.
        /// </summary>
        /// <param name="font">The current font face.</param>
        /// <param name="tokenType">The type of the current token.</param>
        /// <param name="tokenText">The text of the current token.</param>
        /// <param name="tokenNext">The next token after the current token, excluding command tokens.</param>
        /// <returns>The size of the specified token in pixels.</returns>
        private Size2 MeasureToken(SpriteFontFace font, TextParserTokenType tokenType, StringSegment tokenText, TextParserToken? tokenNext = null)
        {
            switch (tokenType)
            {
                case TextParserTokenType.Icon:
                    {
                        TextIconInfo icon;
                        if (!registeredIcons.TryGetValue(tokenText, out icon))
                            throw new InvalidOperationException(UltravioletStrings.UnrecognizedIcon.Format(tokenText));

                        return new Size2(icon.Width ?? icon.Icon.Controller.Width, icon.Height ?? icon.Icon.Controller.Height);
                    }

                case TextParserTokenType.Text:
                    {
                        var size = font.MeasureString(tokenText);
                        if (tokenNext.HasValue)
                        {
                            var tokenNextValue = tokenNext.GetValueOrDefault();
                            if (tokenNextValue.TokenType == TextParserTokenType.Text && !tokenNextValue.Text.IsEmpty && !tokenNextValue.IsNewLine)
                            {
                                var charLast = tokenText[tokenText.Length - 1];
                                var charNext = tokenNextValue.Text[0];
                                var kerning = font.Kerning.Get(charLast, charNext);
                                return new Size2(size.Width + kerning, size.Height);
                            }
                        }
                        return size;
                    }
            }
            return Size2.Zero;
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Given a string and an available space, returns the largest substring which will fit within that space.
        /// </summary>
        private Boolean GetFittedSubstring(SpriteFontFace font, Int32 maxLineWidth, ref StringSegment tokenText, ref Size2 tokenSize, ref LayoutState state, Boolean hyphenate)
        {
            var substringAvailableWidth = maxLineWidth - state.PositionX;
            var substringWidth = 0;
            var substringLength = 0;

            for (int glyphIndex = 0; glyphIndex < tokenText.Length - 1; glyphIndex++)
            {
                var glyph1 = tokenText[glyphIndex];
                var glyph2 = tokenText[glyphIndex + 1];
                var glyphWidth = 0;

                if (hyphenate)
                {
                    glyphWidth = font.MeasureGlyph(glyph1, '-').Width + font.MeasureGlyph('-').Width;
                }
                else
                {
                    glyphWidth = font.MeasureGlyph(glyph1).Width;
                }

                if (substringAvailableWidth - glyphWidth < 0)
                    break;

                var glyphSize = font.MeasureGlyph(glyph1, glyph2);
                substringAvailableWidth -= glyphSize.Width;
                substringWidth += glyphSize.Width;
                substringLength++;
            }

            tokenText = substringLength > 0 ? tokenText.Substring(0, substringLength) : StringSegment.Empty;
            tokenSize = new Size2(substringWidth, tokenSize.Height);

            return substringLength > 0;
        }