Inheritance: MarkdownInline
        /// <summary>
        /// Attempts to parse a markdown link e.g. "[](http://www.reddit.com)".
        /// </summary>
        /// <param name="markdown"> The markdown text. </param>
        /// <param name="start"> The location to start parsing. </param>
        /// <param name="maxEnd"> The location to stop parsing. </param>
        /// <returns> A parsed markdown link, or <c>null</c> if this is not a markdown link. </returns>
        internal static Common.InlineParseResult Parse(string markdown, int start, int maxEnd)
        {
            // Expect a '[' character.
            if (start == maxEnd || markdown[start] != '[')
                return null;

            // Find the ']' character, keeping in mind that [test [0-9]](http://www.test.com) is allowed.
            int linkTextOpen = start + 1;
            int pos = linkTextOpen;
            int linkTextClose;
            int openSquareBracketCount = 0;
            while (true)
            {
                linkTextClose = markdown.IndexOfAny(new char[] { '[', ']' }, pos, maxEnd - pos);
                if (linkTextClose == -1)
                    return null;
                if (markdown[linkTextClose] == '[')
                    openSquareBracketCount++;
                else if (openSquareBracketCount > 0)
                    openSquareBracketCount--;
                else
                    break;
                pos = linkTextClose + 1;
            }

            // Skip whitespace.
            pos = linkTextClose + 1;
            while (pos < maxEnd && Common.IsWhiteSpace(markdown[pos]))
                pos++;
            if (pos == maxEnd)
                return null;

            // Expect the '(' character or the '[' character.
            int linkOpen = pos;
            if (markdown[pos] == '(')
            {
                // Skip whitespace.
                linkOpen++;
                while (linkOpen < maxEnd && Common.IsWhiteSpace(markdown[linkOpen]))
                    linkOpen++;

                // Find the ')' character.
                pos = linkOpen;
                int linkClose = -1;
                while (pos < maxEnd)
                {
                    linkClose = Common.IndexOf(markdown, ')', pos, maxEnd);
                    if (linkClose == -1)
                        return null;
                    if (markdown[linkClose - 1] != '\\')
                        break;
                    pos = linkClose + 1;
                }
                if (pos >= maxEnd)
                    return null;
                int end = linkClose + 1;

                // Skip whitespace backwards.
                while (linkClose > linkOpen && Common.IsWhiteSpace(markdown[linkClose - 1]))
                    linkClose--;

                // If there is no text whatsoever, then this is not a valid link.
                if (linkOpen == linkClose)
                    return null;

                // Check if there is tooltip text.
                string url;
                string tooltip = null;
                bool lastUrlCharIsDoubleQuote = markdown[linkClose - 1] == '"';
                int tooltipStart = Common.IndexOf(markdown, " \"", linkOpen, linkClose - 1);
                if (tooltipStart == linkOpen)
                    return null;
                if (lastUrlCharIsDoubleQuote && tooltipStart != -1)
                {
                    // Extract the URL (resolving any escape sequences).
                    url = TextRunInline.ResolveEscapeSequences(markdown, linkOpen, tooltipStart).TrimEnd(' ', '\t', '\r', '\n');
                    tooltip = markdown.Substring(tooltipStart + 2, (linkClose - 1) - (tooltipStart + 2));
                }
                else
                {
                    // Extract the URL (resolving any escape sequences).
                    url = TextRunInline.ResolveEscapeSequences(markdown, linkOpen, linkClose);
                }

                // Check the URL is okay.
                if (!IsUrlValid(url))
                    return null;

                // We found a regular stand-alone link.
                var result = new MarkdownLinkInline();
                result.Inlines = Common.ParseInlineChildren(markdown, linkTextOpen, linkTextClose, ignoreLinks: true);
                result.Url = url.Replace(" ", "%20");
                result.Tooltip = tooltip;
                return new Common.InlineParseResult(result, start, end);
            }
            else if (markdown[pos] == '[')
            {
                // Find the ']' character.
                int linkClose = Common.IndexOf(markdown, ']', pos + 1, maxEnd);
                if (linkClose == -1)
                    return null;

                // We found a reference-style link.
                var result = new MarkdownLinkInline();
                result.Inlines = Common.ParseInlineChildren(markdown, linkTextOpen, linkTextClose, ignoreLinks: true);
                result.ReferenceId = markdown.Substring(linkOpen + 1, linkClose - (linkOpen + 1));
                if (result.ReferenceId == string.Empty)
                    result.ReferenceId = markdown.Substring(linkTextOpen, linkTextClose - linkTextOpen);
                return new Common.InlineParseResult(result, start, linkClose + 1);
            }
            return null;
        }
        /// <summary>
        /// Renders a link element
        /// </summary>
        /// <param name="inlineCollection"> The list to add to. </param>
        /// <param name="element"> The parsed inline element to render. </param>
        /// <param name="parent"> The container element. </param>
        /// <param name="context"> Persistent state. </param>
        private void RenderMarkdownLink(InlineCollection inlineCollection, MarkdownLinkInline element, TextElement parent, RenderContext context)
        {
            // Avoid crash when link text is empty.
            if (element.Inlines.Count == 0)
                return;

            // Attempt to resolve references.
            element.ResolveReference(this.document);
            if (element.Url == null)
            {
                // The element couldn't be resolved, just render it as text.
                RenderInlineChildren(inlineCollection, element.Inlines, parent, context);
                return;
            }

            // HACK: Superscript is not allowed within a hyperlink.  But if we switch it around, so
            // that the superscript is outside the hyperlink, then it will render correctly.
            // This assumes that the entire hyperlink is to be rendered as superscript.
            if (AllTextIsSuperscript(element) == false)
            {
                // Regular ol' hyperlink.
                var link = new Hyperlink();

                // Register the link
                this.linkRegister.RegisterNewHyperLink(link, element.Url);

                // Remove superscripts.
                RemoveSuperscriptRuns(element, insertCaret: true);

                // Render the children into the link inline.
                var childContext = context.Clone();
                childContext.WithinHyperlink = true;
                RenderInlineChildren(link.Inlines, element.Inlines, link, childContext);
                context.TrimLeadingWhitespace = childContext.TrimLeadingWhitespace;

                // Add it to the current inlines
                inlineCollection.Add(link);
            }
            else
            {
                // THE HACK IS ON!

                // Create a fake superscript element.
                var fakeSuperscript = new SuperscriptTextInline();
                fakeSuperscript.Inlines = new List<MarkdownInline> { element };

                // Remove superscripts.
                RemoveSuperscriptRuns(element, insertCaret: false);

                // Now render it.
                RenderSuperscriptRun(inlineCollection, fakeSuperscript, parent, context);

            }
        }
Example #3
0
        /// <summary>
        /// Attempts to parse a markdown link e.g. "[](http://www.reddit.com)".
        /// </summary>
        /// <param name="markdown"> The markdown text. </param>
        /// <param name="start"> The location to start parsing. </param>
        /// <param name="maxEnd"> The location to stop parsing. </param>
        /// <returns> A parsed markdown link, or <c>null</c> if this is not a markdown link. </returns>
        internal static Common.InlineParseResult Parse(string markdown, int start, int maxEnd)
        {
            // Expect a '[' character.
            if (start == maxEnd || markdown[start] != '[')
            {
                return(null);
            }

            // Find the ']' character, keeping in mind that [test [0-9]](http://www.test.com) is allowed.
            int linkTextOpen = start + 1;
            int pos          = linkTextOpen;
            int linkTextClose;
            int openSquareBracketCount = 0;

            while (true)
            {
                linkTextClose = markdown.IndexOfAny(new char[] { '[', ']' }, pos, maxEnd - pos);
                if (linkTextClose == -1)
                {
                    return(null);
                }
                if (markdown[linkTextClose] == '[')
                {
                    openSquareBracketCount++;
                }
                else if (openSquareBracketCount > 0)
                {
                    openSquareBracketCount--;
                }
                else
                {
                    break;
                }
                pos = linkTextClose + 1;
            }

            // Skip whitespace.
            pos = linkTextClose + 1;
            while (pos < maxEnd && Common.IsWhiteSpace(markdown[pos]))
            {
                pos++;
            }
            if (pos == maxEnd)
            {
                return(null);
            }

            // Expect the '(' character or the '[' character.
            int linkOpen = pos;

            if (markdown[pos] == '(')
            {
                // Skip whitespace.
                linkOpen++;
                while (linkOpen < maxEnd && Common.IsWhiteSpace(markdown[linkOpen]))
                {
                    linkOpen++;
                }

                // Find the ')' character.
                pos = linkOpen;
                int linkClose = -1;
                while (pos < maxEnd)
                {
                    linkClose = Common.IndexOf(markdown, ')', pos, maxEnd);
                    if (linkClose == -1)
                    {
                        return(null);
                    }
                    if (markdown[linkClose - 1] != '\\')
                    {
                        break;
                    }
                    pos = linkClose + 1;
                }
                if (pos >= maxEnd)
                {
                    return(null);
                }
                int end = linkClose + 1;

                // Skip whitespace backwards.
                while (linkClose > linkOpen && Common.IsWhiteSpace(markdown[linkClose - 1]))
                {
                    linkClose--;
                }

                // If there is no text whatsoever, then this is not a valid link.
                if (linkOpen == linkClose)
                {
                    return(null);
                }

                // Check if there is tooltip text.
                string url;
                string tooltip = null;
                bool   lastUrlCharIsDoubleQuote = markdown[linkClose - 1] == '"';
                int    tooltipStart             = Common.IndexOf(markdown, " \"", linkOpen, linkClose - 1);
                if (tooltipStart == linkOpen)
                {
                    return(null);
                }
                if (lastUrlCharIsDoubleQuote && tooltipStart != -1)
                {
                    // Extract the URL (resolving any escape sequences).
                    url     = TextRunInline.ResolveEscapeSequences(markdown, linkOpen, tooltipStart).TrimEnd(' ', '\t', '\r', '\n');
                    tooltip = markdown.Substring(tooltipStart + 2, (linkClose - 1) - (tooltipStart + 2));
                }
                else
                {
                    // Extract the URL (resolving any escape sequences).
                    url = TextRunInline.ResolveEscapeSequences(markdown, linkOpen, linkClose);
                }

                // Check the URL is okay.
                if (!IsUrlValid(url))
                {
                    return(null);
                }

                // We found a regular stand-alone link.
                var result = new MarkdownLinkInline();
                result.Inlines = Common.ParseInlineChildren(markdown, linkTextOpen, linkTextClose, ignoreLinks: true);
                result.Url     = url.Replace(" ", "%20");
                result.Tooltip = tooltip;
                return(new Common.InlineParseResult(result, start, end));
            }
            else if (markdown[pos] == '[')
            {
                // Find the ']' character.
                int linkClose = Common.IndexOf(markdown, ']', pos + 1, maxEnd);
                if (linkClose == -1)
                {
                    return(null);
                }

                // We found a reference-style link.
                var result = new MarkdownLinkInline();
                result.Inlines     = Common.ParseInlineChildren(markdown, linkTextOpen, linkTextClose, ignoreLinks: true);
                result.ReferenceId = markdown.Substring(linkOpen + 1, linkClose - (linkOpen + 1));
                if (result.ReferenceId == string.Empty)
                {
                    result.ReferenceId = markdown.Substring(linkTextOpen, linkTextClose - linkTextOpen);
                }
                return(new Common.InlineParseResult(result, start, linkClose + 1));
            }
            return(null);
        }
        /// <summary>
        /// Renders a link element
        /// </summary>
        /// <param name="element"></param>
        /// <param name="currentInlines"></param>
        /// <param name="trimTextStart">If true this element should trin the start of the text and set to fales.</param>
        private void RenderMarkdownLink(MarkdownLinkInline element, InlineCollection currentInlines, ref bool trimTextStart)
        {
            // Create the text run
            Hyperlink link = new Hyperlink();

            // Register the link
            m_linkRegister.RegisterNewHyperLink(link, element.Url);

            // Render the children into the link inline.
            RenderInlineChildren(element, link.Inlines, ref trimTextStart);

            // Add it to the current inlines
            currentInlines.Add(link);
        }