Example #1
0
        private static string ToPlainText_Slow(AbstractString richText, int firstDollarOffset)
        {
            var result = new StringBuilder();

            for (int i = 0; i < firstDollarOffset; i++)
            {
                result.Append(richText[i]);
            }

            int  rangeStarted = 0;
            char?closer       = null;

            for (int i = firstDollarOffset, l = richText.Length; i < l; i++)
            {
                var ch   = richText[i];
                var next = (i < l - 1) ? richText[i + 1] : '\0';
                if (closer != null)
                {
                    if (ch == closer)
                    {
                        if (closer == ')')
                        {
                            var markedText = richText.Substring(rangeStarted, i - rangeStarted);
                            var pipeOffset = markedText.IndexOf('|');
                            if (pipeOffset >= 0)
                            {
                                result.Append(markedText.Substring(pipeOffset + 1));
                            }
                            else
                            {
                                result.Append(markedText);
                            }
                        }
                        closer = null;
                    }
                    continue;
                }
                else if (ch == '$')
                {
                    if ((next == '(') || (next == '['))
                    {
                        closer = (next == '(') ? ')' : ']';
                        i++;
                        rangeStarted = i + 1;
                        continue;
                    }
                }
                else
                {
                    result.Append(ch);
                }
            }

            return(result.ToString());
        }
Example #2
0
        public static DenseList <RichRule> ParseRules(AbstractString text, ref DenseList <RichParseError> parseErrors)
        {
            var result = new DenseList <RichRule>();
            int keyStart = 0;
            int?keyEnd = null, valueStart = null;

            for (int i = 0, l = text.Length; i <= l; i++)
            {
                var ch = (i == l) ? '\0' : text[i];
                switch (ch)
                {
                case ':':
                    if (valueStart.HasValue)
                    {
                        parseErrors.Add(new RichParseError {
                            Text    = text,
                            Offset  = i,
                            Message = "Unexpected :"
                        });
                    }
                    else
                    {
                        keyEnd     = i;
                        valueStart = i + 1;
                    }
                    break;

                case '\0':
                case ';':
                    if (!valueStart.HasValue)
                    {
                        if (ch != '\0')
                        {
                            parseErrors.Add(new RichParseError {
                                Text    = text,
                                Offset  = i,
                                Message = "Unexpected ;"
                            });
                        }
                    }
                    else
                    {
                        result.Add(new RichRule {
                            Key   = new AbstractString(in text, keyStart, keyEnd.Value - keyStart),
                            Value = new AbstractString(in text, valueStart.Value, i - valueStart.Value),
                        });
                        keyStart = i + 1;
                        keyEnd   = valueStart = null;
                    }
                    break;
                }
Example #3
0
        public static string ToPlainText(AbstractString richText)
        {
            if (richText.IsNull)
            {
                return(null);
            }

            for (int i = 0, l = richText.Length; i < l - 1; i++)
            {
                var c = richText[i];
                if ((c == '$') && (richText[i + 1] == '[') || (richText[i + 1] == '('))
                {
                    return(ToPlainText_Slow(richText, i));
                }
            }

            return(richText.ToString());
        }
Example #4
0
 public DynamicStringLayout(IGlyphSource font = null, string text = "")
 {
     _GlyphSource = font;
     _Text        = text;
 }
Example #5
0
 public DynamicStringLayout(SpriteFont font, string text = "")
 {
     _GlyphSource = new SpriteFontGlyphSource(font);
     _Text        = text;
 }
        public StringLayout WordWrap(AbstractString text, float wrapAtX, ArraySegment <BitmapDrawCall>?buffer = null, float wrapIndentation = 0f, bool characterWrap = true)
        {
            int?  thisWordStartIndex     = null;
            int   indexOfFirstCharInLine = 0;
            float?previousCharacterX     = null;
            float thisWordWidth          = 0;
            var   newSize = new Vector2();

            ArraySegment <BitmapDrawCall> _buffer;

            if (buffer.HasValue)
            {
                _buffer = buffer.Value;
            }
            else
            {
                _buffer = new ArraySegment <BitmapDrawCall>(new BitmapDrawCall[Count]);
            }

            Array.Copy(this.DrawCalls.Array, this.DrawCalls.Offset, _buffer.Array, _buffer.Offset, Count);

            for (var i = 0; i < Count; i++)
            {
                var ch = text[i];
                var dc = _buffer.Array[_buffer.Offset + i];

                // Detect line break
                if (previousCharacterX.HasValue && (dc.Position.X <= previousCharacterX.Value))
                {
                    indexOfFirstCharInLine = i;
                }

                var isWordChar = Char.IsLetterOrDigit(ch) || (ch == '\'');
                // Start out using texture width (not entirely accurate)
                var thisCharWidth = dc.TextureRegion.Size.X * dc.Texture.Width;
                // Then if we can, use the gap between this char and next char
                if (i < (Count - 1))
                {
                    var nextDrawCall = _buffer.Array[_buffer.Offset + i + 1];
                    // Make sure the next draw call wasn't wrapped
                    if (nextDrawCall.Position.X > dc.Position.X)
                    {
                        thisCharWidth = nextDrawCall.Position.X - dc.Position.X;
                    }
                }
                thisWordWidth += thisCharWidth;

                var needWrap = (dc.Position.X >= wrapAtX) &&
                               !(ch == 10 || ch == 13 || ch == ' ');

                if (needWrap)
                {
                    int fromOffset = i;

                    if (thisWordStartIndex.HasValue && (thisWordWidth <= (wrapAtX - wrapIndentation)))
                    {
                        // We have a current word and it's not too wide to fit on its own line.
                        fromOffset = thisWordStartIndex.Value;
                    }
                    else if (characterWrap)
                    {
                        // We can character wrap, so continue with a fromOffset of i
                    }
                    else
                    {
                        // Character wrap disallowed, so don't wrap. The user will probably scale the output layout.
                        needWrap = false;
                    }

                    // We'd be wrapping the whole line, which is pointless.
                    if (indexOfFirstCharInLine == fromOffset)
                    {
                        needWrap = false;
                    }

                    if (needWrap)
                    {
                        float xDelta = Position.X - _buffer.Array[_buffer.Offset + fromOffset].Position.X + wrapIndentation;
                        indexOfFirstCharInLine = fromOffset;

                        float firstX       = _buffer.Array[_buffer.Offset + fromOffset].Position.X;
                        bool  didBreakLine = false;

                        for (var j = fromOffset; j < Count; j++)
                        {
                            var dc2 = _buffer.Array[_buffer.Offset + j];

                            if ((dc2.Position.X <= firstX) && (j > fromOffset))
                            {
                                didBreakLine = true;
                            }

                            if (!didBreakLine)
                            {
                                dc2.Position.X += xDelta;
                            }

                            dc2.Position.Y += LineHeight;
                            _buffer.Array[_buffer.Offset + j] = dc2;
                        }
                    }
                }

                if (!isWordChar)
                {
                    if (thisWordStartIndex.HasValue)
                    {
                        thisWordStartIndex = null;
                    }
                }
                else
                {
                    if (!thisWordStartIndex.HasValue)
                    {
                        thisWordStartIndex = i;
                        thisWordWidth      = 0f;
                    }
                }

                previousCharacterX = dc.Position.X;
            }

            for (var i = 0; i < Count; i++)
            {
                var dc = _buffer.Array[_buffer.Offset + i];
                newSize.X = Math.Max(
                    dc.Position.X + (dc.Texture.Width * dc.TextureRegion.Size.X) - Position.X, newSize.X
                    );
                newSize.Y = Math.Max(
                    dc.Position.Y + (dc.Texture.Height * dc.TextureRegion.Size.Y) - Position.Y, newSize.Y
                    );
            }

            var segment = new ArraySegment <BitmapDrawCall>(_buffer.Array, _buffer.Offset, Count);

            if (segment.Count > text.Length)
            {
                throw new InvalidDataException();
            }

            return(new StringLayout(
                       Position, newSize,
                       LineHeight,
                       // FIXME
                       FirstCharacterBounds,
                       LastCharacterBounds,
                       segment
                       ));
        }
 public DynamicStringLayout(SpriteFont font, string text = "")
 {
     _Font = font;
     _Text = text;
 }
Example #8
0
        public ArraySegment <BitmapDrawCall> AppendText(
            IGlyphSource font, AbstractString text,
            Dictionary <char, KerningAdjustment> kerningAdjustments = null
            )
        {
            if (!IsInitialized)
            {
                throw new InvalidOperationException("Call Initialize first");
            }

            if (font == null)
            {
                throw new ArgumentNullException("font");
            }
            if (text.IsNull)
            {
                throw new ArgumentNullException("text");
            }

            EnsureBufferCapacity(bufferWritePosition + text.Length);

            if (kerningAdjustments == null)
            {
                kerningAdjustments = StringLayout.GetDefaultKerningAdjustments(font);
            }

            var effectiveScale = scale / font.DPIScaleFactor;

            var drawCall = default(BitmapDrawCall);

            drawCall.MultiplyColor = color.GetValueOrDefault(Color.White);
            drawCall.ScaleF        = effectiveScale;
            drawCall.SortKey       = sortKey;

            float x = 0;
            float?defaultLineSpacing = null;

            for (int i = 0, l = text.Length; i < l; i++)
            {
                var  ch = text[i];
                bool isWhiteSpace = char.IsWhiteSpace(ch),
                     forcedWrap = false, lineBreak = false,
                     deadGlyph = false;
                Glyph             glyph;
                KerningAdjustment kerningAdjustment;

                if (ch == '\r')
                {
                    if (((i + 1) < l) && (text[i + 1] == '\n'))
                    {
                        i += 1;
                    }

                    lineBreak = true;
                }
                else if (ch == '\n')
                {
                    lineBreak = true;
                }

                if (isWhiteSpace)
                {
                    wordStartWritePosition = -1;
                    wordWrapSuppressed     = false;
                }
                else
                {
                    if (wordStartWritePosition < 0)
                    {
                        wordStartWritePosition = bufferWritePosition;
                        wordStartOffset        = characterOffset;
                    }
                }

                deadGlyph = !font.GetGlyph(ch, out glyph);

                float effectiveLineSpacing = glyph.LineSpacing;
                if (deadGlyph)
                {
                    if (currentLineSpacing.HasValue)
                    {
                        effectiveLineSpacing = currentLineSpacing.Value;
                    }
                    else if (defaultLineSpacing.HasValue)
                    {
                        effectiveLineSpacing = defaultLineSpacing.Value;
                    }
                    else
                    {
                        Glyph space;
                        if (font.GetGlyph(' ', out space))
                        {
                            defaultLineSpacing = effectiveLineSpacing = space.LineSpacing;
                        }
                    }
                }

                if ((kerningAdjustments != null) && kerningAdjustments.TryGetValue(ch, out kerningAdjustment))
                {
                    glyph.LeftSideBearing  += kerningAdjustment.LeftSideBearing;
                    glyph.Width            += kerningAdjustment.Width;
                    glyph.RightSideBearing += kerningAdjustment.RightSideBearing;
                }

                x =
                    characterOffset.X +
                    glyph.LeftSideBearing +
                    glyph.RightSideBearing +
                    glyph.Width + glyph.CharacterSpacing;

                if ((x * effectiveScale) >= lineBreakAtX)
                {
                    if (
                        !deadGlyph &&
                        (colIndex > 0) &&
                        !isWhiteSpace
                        )
                    {
                        forcedWrap = true;
                    }
                }

                if (forcedWrap)
                {
                    var currentWordSize = x - wordStartOffset.X;

                    if (wordWrap && !wordWrapSuppressed && (currentWordSize * effectiveScale <= lineBreakAtX))
                    {
                        WrapWord(buffer, wordStartOffset, wordStartWritePosition, bufferWritePosition - 1, effectiveScale, effectiveLineSpacing);
                        wordWrapSuppressed = true;
                        lineBreak          = true;
                    }
                    else if (characterWrap)
                    {
                        characterOffset.X  = xOffsetOfWrappedLine;
                        characterOffset.Y += effectiveLineSpacing;

                        maxX = Math.Max(maxX, currentLineMaxX * effectiveScale);
                        wordStartWritePosition = bufferWritePosition;
                        wordStartOffset        = characterOffset;
                        lineBreak = true;
                    }
                }

                if (lineBreak)
                {
                    if (!forcedWrap)
                    {
                        characterOffset.X  = xOffsetOfNewLine;
                        characterOffset.Y += effectiveLineSpacing;
                        maxX = Math.Max(maxX, currentLineMaxX * effectiveScale);
                    }

                    initialLineXOffset            = characterOffset.X;
                    currentLineMaxX               = 0;
                    currentLineWhitespaceMaxX     = 0;
                    currentLineWhitespaceMaxXLeft = 0;
                    rowIndex += 1;
                    colIndex  = 0;
                }

                if (deadGlyph)
                {
                    characterSkipCount--;
                    if (characterLimit.HasValue)
                    {
                        characterLimit--;
                    }
                    continue;
                }

                // HACK: Recompute after wrapping
                x =
                    characterOffset.X +
                    glyph.LeftSideBearing +
                    glyph.RightSideBearing +
                    glyph.Width + glyph.CharacterSpacing;

                characterOffset.X += glyph.CharacterSpacing;

                lastCharacterBounds = Bounds.FromPositionAndSize(
                    characterOffset * effectiveScale, new Vector2(
                        glyph.LeftSideBearing + glyph.Width + glyph.RightSideBearing,
                        glyph.LineSpacing
                        ) * effectiveScale
                    );

                if ((rowIndex == 0) && (colIndex == 0))
                {
                    firstCharacterBounds = lastCharacterBounds;
                }

                characterOffset.X += glyph.LeftSideBearing;

                if (colIndex == 0)
                {
                    characterOffset.X = Math.Max(characterOffset.X, 0);
                }

                if (characterSkipCount <= 0)
                {
                    if (characterLimit.HasValue && characterLimit.Value <= 0)
                    {
                        break;
                    }

                    var glyphPosition = new Vector2(
                        actualPosition.X + (glyph.XOffset + characterOffset.X) * effectiveScale,
                        actualPosition.Y + (glyph.YOffset + characterOffset.Y) * effectiveScale
                        );

                    if (!isWhiteSpace)
                    {
                        if (bufferWritePosition >= buffer.Count)
                        {
                            EnsureBufferCapacity(bufferWritePosition);
                        }

                        drawCall.Texture       = glyph.Texture;
                        drawCall.TextureRegion = glyph.Texture.BoundsFromRectangle(ref glyph.BoundsInTexture);
                        Snap(glyphPosition, out drawCall.Position);

                        // HACK so that the alignment pass can detect rows. We strip this later.
                        if (alignment != HorizontalAlignment.Left)
                        {
                            drawCall.SortKey.Order = rowIndex;
                        }

                        buffer.Array[buffer.Offset + bufferWritePosition] = drawCall;

                        currentLineMaxX = Math.Max(currentLineMaxX, x);
                        maxY            = Math.Max(maxY, (characterOffset.Y + effectiveLineSpacing) * effectiveScale);

                        bufferWritePosition += 1;
                        drawCallsWritten    += 1;
                    }
                    else
                    {
                        currentLineWhitespaceMaxXLeft = Math.Max(currentLineWhitespaceMaxXLeft, characterOffset.X);
                        currentLineWhitespaceMaxX     = Math.Max(currentLineWhitespaceMaxX, x);
                    }

                    characterLimit--;
                }
                else
                {
                    characterSkipCount--;
                }

                characterOffset.X += (glyph.Width + glyph.RightSideBearing);
                currentLineSpacing = glyph.LineSpacing;
                maxLineSpacing     = Math.Max(maxLineSpacing, effectiveLineSpacing);

                colIndex += 1;
            }

            var segment = new ArraySegment <BitmapDrawCall>(
                buffer.Array, buffer.Offset, drawCallsWritten
                );

            maxX = Math.Max(maxX, currentLineMaxX * effectiveScale);

            return(segment);
        }