/// <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 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 && ParseHelpers.IsMarkdownWhiteSpace(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 && ParseHelpers.IsMarkdownWhiteSpace(markdown[linkOpen])) { linkOpen++; } // Find the ')' character. pos = linkOpen; int linkClose = -1; var openParenthesis = 0; while (pos < maxEnd) { if (markdown[pos] == ')') { if (openParenthesis == 0) { linkClose = pos; break; } else { openParenthesis--; } } if (markdown[pos] == '(') { openParenthesis++; } pos++; } if (pos >= maxEnd) { return(null); } int end = linkClose + 1; // Skip whitespace backwards. while (linkClose > linkOpen && ParseHelpers.IsMarkdownWhiteSpace(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 (!url.IsEmail()) { if (!Common.IsUrlValid(url)) { return(null); } } else { tooltip = url = string.Format("mailto:{0}", url); } // 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 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 InlineParseResult(result, start, linkClose + 1)); } return(null); }
/// <summary> /// Attempts to parse an image e.g. "![Toolkit logo](https://raw.githubusercontent.com/Microsoft/UWPCommunityToolkit/master/Microsoft.Toolkit.Uwp.SampleApp/Assets/ToolkitLogo.png)". /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="start"> The location to start parsing. </param> /// <param name="end"> The location to stop parsing. </param> /// <returns> A parsed markdown image, or <c>null</c> if this is not a markdown image. </returns> internal static InlineParseResult Parse(string markdown, int start, int end) { // Expect a '!' character. if (start >= end || markdown[start] != '!') { return(null); } int pos = start + 1; // Then a '[' character if (pos >= end || markdown[pos] != '[') { return(null); } pos++; // Find the ']' character while (pos < end) { if (markdown[pos] == ']') { break; } pos++; } if (pos == end) { return(null); } // Extract the alt. string tooltip = markdown.Substring(start + 2, pos - (start + 2)); // Expect the '(' character. pos++; if (pos == end || markdown[pos] != '(') { return(null); } // Skip whitespace pos++; while (pos < end && ParseHelpers.IsMarkdownWhiteSpace(markdown[pos])) { pos++; } if (pos == end) { return(null); } // Extract the URL. int urlStart = pos; while (pos < end && markdown[pos] != ')') { pos++; } var imageDimensionsPos = markdown.IndexOf(" =", urlStart, pos - urlStart, StringComparison.Ordinal); var url = imageDimensionsPos > 0 ? TextRunInline.ResolveEscapeSequences(markdown, urlStart, imageDimensionsPos) : TextRunInline.ResolveEscapeSequences(markdown, urlStart, pos); int imageWidth = 0; int imageHeight = 0; if (imageDimensionsPos > 0) { // trying to find 'x' which separates image width and height var dimensionsSepatorPos = markdown.IndexOf("x", imageDimensionsPos + 2, pos - imageDimensionsPos - 1, StringComparison.Ordinal); // didn't find separator, trying to parse value as imageWidth if (dimensionsSepatorPos == -1) { var imageWidthStr = markdown.Substring(imageDimensionsPos + 2, pos - imageDimensionsPos - 2); int.TryParse(imageWidthStr, out imageWidth); } else { var dimensions = markdown.Substring(imageDimensionsPos + 2, pos - imageDimensionsPos - 2).Split('x'); // got width and height if (dimensions.Length == 2) { int.TryParse(dimensions[0], out imageWidth); int.TryParse(dimensions[1], out imageHeight); } } } // We found something! var result = new ImageInline { Tooltip = tooltip, Url = url, Text = markdown.Substring(start, pos + 1 - start), ImageWidth = imageWidth, ImageHeight = imageHeight }; return(new InlineParseResult(result, start, pos + 1)); }