示例#1
0
        /// <summary>Initializes a new instance of the <see cref="HtmlFormatter" /> class.</summary>
        /// <param name="target">The target text writer.</param>
        /// <param name="settings">The settings used when formatting the data.</param>
        /// <exception cref="ArgumentNullException">when <paramref name="target"/> is <see langword="null"/></exception>
        public HtmlFormatter(TextWriter target, CommonMarkSettings settings)
        {
            if (target == null)
                throw new ArgumentNullException(nameof(target));

            if (settings == null)
                settings = CommonMarkSettings.Default;

            _target = new HtmlTextWriter(target);
            _settings = settings;
        }
示例#2
0
    /// <summary>
    /// Escapes special URL characters.
    /// </summary>
    /// <remarks>Orig: escape_html(inp, preserve_entities)</remarks>
    internal static void EscapeUrl(string input, HtmlTextWriter target) {
      if (input == null)
        return;

      char c;
      int lastPos = 0;
      int len = input.Length;
      char[] buffer;

      if (target.Buffer.Length < len)
        buffer = target.Buffer = input.ToCharArray();
      else {
        buffer = target.Buffer;
        input.CopyTo(0, buffer, 0, len);
      }

      // since both \r and \n are not url-safe characters and will be encoded, all calls are
      // made to WriteConstant.
      for (var pos = 0; pos < len; pos++) {
        c = buffer[pos];

        if (c == '&') {
          target.WriteConstant(buffer, lastPos, pos - lastPos);
          lastPos = pos + 1;
          target.WriteConstant(EscapeHtmlAmpersand);
        } else if (c < 128 && !UrlSafeCharacters[c]) {
          target.WriteConstant(buffer, lastPos, pos - lastPos);
          lastPos = pos + 1;

          target.WriteConstant(new[] { '%', HexCharacters[c / 16], HexCharacters[c % 16] });
        } else if (c > 127) {
          target.WriteConstant(buffer, lastPos, pos - lastPos);
          lastPos = pos + 1;

          byte[] bytes;
          if (c >= '\ud800' && c <= '\udfff' && len != lastPos) {
            // this char is the first of UTF-32 character pair
            bytes = Encoding.UTF8.GetBytes(new[] { c, buffer[lastPos] });
            lastPos = ++pos + 1;
          } else {
            bytes = Encoding.UTF8.GetBytes(new[] { c });
          }

          for (var i = 0; i < bytes.Length; i++)
            target.WriteConstant(new[] { '%', HexCharacters[bytes[i] / 16], HexCharacters[bytes[i] % 16] });
        }
      }

      target.WriteConstant(buffer, lastPos, len - lastPos);
    }
        /// <summary>
        /// Writes the inline list to the given writer as HTML code. 
        /// </summary>
        private static void InlinesToHtml(HtmlTextWriter writer, Inline inline, CommonMarkSettings settings, Stack<InlineStackEntry> stack)
        {
            var uriResolver = settings.UriResolver;
            bool withinLink = false;
            bool stackWithinLink = false;
            bool visitChildren;
            bool trackPositions = settings.TrackSourcePosition;
            string stackLiteral = null;

            while (inline != null)
            {
                visitChildren = false;

                switch (inline.Tag)
                {
                    case InlineTag.String:
                        if (trackPositions)
                        {
                            writer.WriteConstant("<span");
                            PrintPosition(writer, inline);
                            writer.Write('>');
                            EscapeHtml(inline.LiteralContentValue, writer);
                            writer.WriteConstant("</span>");
                        }
                        else
                        {
                            EscapeHtml(inline.LiteralContentValue, writer);
                        }

                        break;

                    case InlineTag.LineBreak:
                        writer.WriteLineConstant("<br />");
                        break;

                    case InlineTag.SoftBreak:
                        if (settings.RenderSoftLineBreaksAsLineBreaks)
                            writer.WriteLineConstant("<br />");
                        else
                            writer.WriteLine();
                        break;

                    case InlineTag.Code:
                        writer.WriteConstant("<code");
                        if (trackPositions) PrintPosition(writer, inline);
                        writer.Write('>');
                        EscapeHtml(inline.LiteralContentValue, writer);
                        writer.WriteConstant("</code>");
                        break;

                    case InlineTag.RawHtml:
                        // cannot output source position for HTML blocks
                        writer.Write(inline.LiteralContentValue);
                        break;

                    case InlineTag.Link:
                        if (withinLink)
                        {
                            writer.Write('[');
                            stackLiteral = "]";
                            stackWithinLink = true;
                            visitChildren = true;
                        }
                        else
                        {
                            writer.WriteConstant("<a href=\"");
                            if (uriResolver != null)
                                EscapeUrl(uriResolver(inline.TargetUrl), writer);
                            else
                                EscapeUrl(inline.TargetUrl, writer);

                            writer.Write('\"');
                            if (inline.LiteralContentValue.Length > 0)
                            {
                                writer.WriteConstant(" title=\"");
                                EscapeHtml(inline.LiteralContentValue, writer);
                                writer.Write('\"');
                            }

                            if (trackPositions) PrintPosition(writer, inline);

                            writer.Write('>');

                            visitChildren = true;
                            stackWithinLink = true;
                            stackLiteral = "</a>";
                        }
                        break;

                    case InlineTag.Image:
                        writer.WriteConstant("<img src=\"");
                        if (uriResolver != null)
                            EscapeUrl(uriResolver(inline.TargetUrl), writer);
                        else
                            EscapeUrl(inline.TargetUrl, writer);

                        writer.WriteConstant("\" alt=\"");
                        InlinesToPlainText(writer, inline.FirstChild, stack);
                        writer.Write('\"');
                        if (inline.LiteralContentValue.Length > 0)
                        {
                            writer.WriteConstant(" title=\"");
                            EscapeHtml(inline.LiteralContentValue, writer);
                            writer.Write('\"');
                        }

                        if (trackPositions) PrintPosition(writer, inline);
                        writer.WriteConstant(" />");

                        break;

                    case InlineTag.Strong:
                        writer.WriteConstant("<strong");
                        if (trackPositions) PrintPosition(writer, inline);
                        writer.Write('>');
                        stackLiteral = "</strong>";
                        stackWithinLink = withinLink;
                        visitChildren = true;
                        break;

                    case InlineTag.Emphasis:
                        writer.WriteConstant("<em");
                        if (trackPositions) PrintPosition(writer, inline);
                        writer.Write('>');
                        stackLiteral = "</em>";
                        visitChildren = true;
                        stackWithinLink = withinLink;
                        break;

                    case InlineTag.Strikethrough:
                        writer.WriteConstant("<del");
                        if (trackPositions) PrintPosition(writer, inline);
                        writer.Write('>');
                        stackLiteral = "</del>";
                        visitChildren = true;
                        stackWithinLink = withinLink;
                        break;

                    default:
                        throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline);
                }

                if (visitChildren)
                {
                    stack.Push(new InlineStackEntry(stackLiteral, inline.NextSibling, withinLink));

                    withinLink = stackWithinLink;
                    inline = inline.FirstChild;
                }
                else if (inline.NextSibling != null)
                {
                    inline = inline.NextSibling;
                }
                else
                {
                    inline = null;
                }

                while (inline == null && stack.Count > 0)
                {
                    var entry = stack.Pop();
                    writer.WriteConstant(entry.Literal);
                    inline = entry.Target;
                    withinLink = entry.IsWithinLink;
                }
            }
        }
        /// <summary>
        /// Writes the inline list to the given writer as plain text (without any HTML tags).
        /// </summary>
        /// <seealso href="https://github.com/jgm/CommonMark/issues/145"/>
        private static void InlinesToPlainText(HtmlTextWriter writer, Inline inline, Stack<InlineStackEntry> stack)
        {
            bool withinLink = false;
            bool stackWithinLink = false; 
            bool visitChildren;
            string stackLiteral = null;
            var origStackCount = stack.Count;

            while (inline != null)
            {
                visitChildren = false;

                switch (inline.Tag)
                {
                    case InlineTag.String:
                    case InlineTag.Code:
                    case InlineTag.RawHtml:
                        EscapeHtml(inline.LiteralContentValue, writer);
                        break;

                    case InlineTag.LineBreak:
                    case InlineTag.SoftBreak:
                        writer.WriteLine();
                        break;

                    case InlineTag.Link:
                        if (withinLink)
                        {
                            writer.Write('[');
                            stackLiteral = "]";
                            visitChildren = true;
                            stackWithinLink = true;
                        }
                        else
                        {
                            visitChildren = true;
                            stackWithinLink = true;
                            stackLiteral = string.Empty;
                        }
                        break;

                    case InlineTag.Image:
                        visitChildren = true;
                        stackWithinLink = true;
                        stackLiteral = string.Empty;
                        break;

                    case InlineTag.Strong:
                    case InlineTag.Emphasis:
                    case InlineTag.Strikethrough:
                        stackLiteral = string.Empty;
                        stackWithinLink = withinLink;
                        visitChildren = true;
                        break;

                    default:
                        throw new CommonMarkException("Inline type " + inline.Tag + " is not supported.", inline);
                }

                if (visitChildren)
                {
                    stack.Push(new InlineStackEntry(stackLiteral, inline.NextSibling, withinLink));

                    withinLink = stackWithinLink;
                    inline = inline.FirstChild;
                }
                else if (inline.NextSibling != null)
                {
                    inline = inline.NextSibling;
                }
                else
                {
                    inline = null;
                }

                while (inline == null && stack.Count > origStackCount)
                {
                    var entry = stack.Pop();
                    writer.WriteConstant(entry.Literal);
                    inline = entry.Target;
                    withinLink = entry.IsWithinLink;
                }
            }
        }
        private static void BlocksToHtmlInner(HtmlTextWriter writer, Block block, CommonMarkSettings settings)
        {
            var stack = new Stack<BlockStackEntry>();
            var inlineStack = new Stack<InlineStackEntry>();
            bool visitChildren;
            string stackLiteral = null;
            bool stackTight = false;
            bool tight = false;
            bool trackPositions = settings.TrackSourcePosition;
            int x;

            while (block != null)
            {
                visitChildren = false;

                switch (block.Tag)
                {
                    case BlockTag.Document:
                        stackLiteral = null;
                        stackTight = false;
                        visitChildren = true;
                        break;

                    case BlockTag.Paragraph:
                        if (tight)
                        {
                            InlinesToHtml(writer, block.InlineContent, settings, inlineStack);
                        }
                        else
                        {
                            writer.EnsureLine();
                            writer.WriteConstant("<p");
                            if (trackPositions) PrintPosition(writer, block);
                            writer.Write('>');
                            InlinesToHtml(writer, block.InlineContent, settings, inlineStack);
                            writer.WriteLineConstant("</p>");
                        }
                        break;

                    case BlockTag.BlockQuote:
                        writer.EnsureLine();
                        writer.WriteConstant("<blockquote");
                        if (trackPositions) PrintPosition(writer, block);
                        writer.WriteLine('>');

                        stackLiteral = "</blockquote>";
                        stackTight = false;
                        visitChildren = true;
                        break;

                    case BlockTag.ListItem:
                        writer.EnsureLine();
                        writer.WriteConstant("<li");
                        if (trackPositions) PrintPosition(writer, block);
                        writer.Write('>');

                        stackLiteral = "</li>";
                        stackTight = tight;
                        visitChildren = true;
                        break;

                    case BlockTag.List:
                        // make sure a list starts at the beginning of the line:
                        writer.EnsureLine();
                        var data = block.ListData;
                        writer.WriteConstant(data.ListType == ListType.Bullet ? "<ul" : "<ol");
                        if (data.Start != 1)
                        {
                            writer.WriteConstant(" start=\"");
                            writer.WriteConstant(data.Start.ToString(CultureInfo.InvariantCulture));
                            writer.Write('\"');
                        }
                        if (trackPositions) PrintPosition(writer, block);
                        writer.WriteLine('>');

                        stackLiteral = data.ListType == ListType.Bullet ? "</ul>" : "</ol>";
                        stackTight = data.IsTight;
                        visitChildren = true;
                        break;

                    case BlockTag.AtxHeader:
                    case BlockTag.SETextHeader:
                        writer.EnsureLine();

                        x = block.HeaderLevel;

                        if (trackPositions)
                        {
                            writer.WriteConstant("<h" + x.ToString(CultureInfo.InvariantCulture));
                            PrintPosition(writer, block);
                            writer.Write('>');
                            InlinesToHtml(writer, block.InlineContent, settings, inlineStack);
                            writer.WriteLineConstant(x > 0 && x < 7 ? HeaderCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">");
                        }
                        else
                        {
                            writer.WriteConstant(x > 0 && x < 7 ? HeaderOpenerTags[x - 1] : "<h" + x.ToString(CultureInfo.InvariantCulture) + ">");
                            InlinesToHtml(writer, block.InlineContent, settings, inlineStack);
                            writer.WriteLineConstant(x > 0 && x < 7 ? HeaderCloserTags[x - 1] : "</h" + x.ToString(CultureInfo.InvariantCulture) + ">");
                        }

                        break;

                    case BlockTag.IndentedCode:
                    case BlockTag.FencedCode:
                        writer.EnsureLine();
                        writer.WriteConstant("<pre><code");
                        if (trackPositions) PrintPosition(writer, block);

                        var info = block.FencedCodeData == null ? null : block.FencedCodeData.Info;
                        if (info != null && info.Length > 0)
                        {
                            x = info.IndexOf(' ');
                            if (x == -1)
                                x = info.Length;

                            writer.WriteConstant(" class=\"language-");
                            EscapeHtml(new StringPart(info, 0, x), writer);
                            writer.Write('\"');
                        }
                        writer.Write('>');
                        EscapeHtml(block.StringContent, writer);
                        writer.WriteLineConstant("</code></pre>");
                        break;

                    case BlockTag.HtmlBlock:
                        // cannot output source position for HTML blocks
                        block.StringContent.WriteTo(writer);

                        break;

                    case BlockTag.HorizontalRuler:
                        if (trackPositions)
                        {
                            writer.WriteConstant("<hr");
                            PrintPosition(writer, block);
                            writer.WriteLine();
                        }
                        else
                        {
                            writer.WriteLineConstant("<hr />");
                        }

                        break;

                    case BlockTag.ReferenceDefinition:
                        break;

                    default:
                        throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block);
                }

                if (visitChildren)
                {
                    stack.Push(new BlockStackEntry(stackLiteral, block.NextSibling, tight));

                    tight = stackTight;
                    block = block.FirstChild;
                }
                else if (block.NextSibling != null)
                {
                    block = block.NextSibling;
                }
                else
                {
                    block = null;
                }

                while (block == null && stack.Count > 0)
                {
                    var entry = stack.Pop();

                    writer.WriteLineConstant(entry.Literal);
                    tight = entry.IsTight;
                    block = entry.Target;
                }
            }
        }
 internal static void PrintPosition(HtmlTextWriter writer, Inline inline)
 {
     writer.WriteConstant(" data-sourcepos=\"");
     writer.WriteConstant(inline.SourcePosition.ToString(CultureInfo.InvariantCulture));
     writer.Write('-');
     writer.WriteConstant(inline.SourceLastPosition.ToString(CultureInfo.InvariantCulture));
     writer.WriteConstant("\"");
 }
 public static void BlocksToHtml(TextWriter writer, Block block, CommonMarkSettings settings)
 {
     var wrapper = new HtmlTextWriter(writer);
     BlocksToHtmlInner(wrapper, block, settings);
 }
        /// <summary>
        /// Escapes special HTML characters.
        /// </summary>
        /// <remarks>Orig: escape_html(inp, preserve_entities)</remarks>
        internal static void EscapeHtml(StringContent inp, HtmlTextWriter target)
        {
            int pos;
            int lastPos;
            char[] buffer = target.Buffer;

            var parts = inp.RetrieveParts();
            for (var i = parts.Offset; i < parts.Offset + parts.Count; i++)
            {
                var part = parts.Array[i];

                if (buffer.Length < part.Length)
                    buffer = target.Buffer = new char[part.Length];

                part.Source.CopyTo(part.StartIndex, buffer, 0, part.Length);

                lastPos = part.StartIndex;
                while ((pos = part.Source.IndexOfAny(EscapeHtmlCharacters, lastPos, part.Length - lastPos + part.StartIndex)) != -1)
                {
                    target.Write(buffer, lastPos - part.StartIndex, pos - lastPos);
                    lastPos = pos + 1;

                    switch (part.Source[pos])
                    {
                        case '<':
                            target.WriteConstant(EscapeHtmlLessThan);
                            break;
                        case '>':
                            target.WriteConstant(EscapeHtmlGreaterThan);
                            break;
                        case '&':
                            target.WriteConstant(EscapeHtmlAmpersand);
                            break;
                        case '"':
                            target.WriteConstant(EscapeHtmlQuote);
                            break;
                    }
                }

                target.Write(buffer, lastPos - part.StartIndex, part.Length - lastPos + part.StartIndex);
            }
        }
        /// <summary>
        /// Escapes special HTML characters.
        /// </summary>
        /// <remarks>Orig: escape_html(inp, preserve_entities)</remarks>
        internal static void EscapeHtml(StringPart input, HtmlTextWriter target)
        {
            if (input.Length == 0)
                return;

            int pos;
            int lastPos = input.StartIndex;
            char[] buffer;

            if (target.Buffer.Length < input.Length)
                buffer = target.Buffer = new char[input.Length];
            else
                buffer = target.Buffer;
                 
            input.Source.CopyTo(input.StartIndex, buffer, 0, input.Length);

            while ((pos = input.Source.IndexOfAny(EscapeHtmlCharacters, lastPos, input.Length - lastPos + input.StartIndex)) != -1)
            {
                target.Write(buffer, lastPos - input.StartIndex, pos - lastPos);
                lastPos = pos + 1;

                switch (input.Source[pos])
                {
                    case '<':
                        target.WriteConstant(EscapeHtmlLessThan);
                        break;
                    case '>':
                        target.WriteConstant(EscapeHtmlGreaterThan);
                        break;
                    case '&':
                        target.WriteConstant(EscapeHtmlAmpersand);
                        break;
                    case '"':
                        target.WriteConstant(EscapeHtmlQuote);
                        break;
                }
            }

            target.Write(buffer, lastPos - input.StartIndex, input.Length - lastPos + input.StartIndex);
        }
示例#10
0
 internal static void PrintPosition(HtmlTextWriter writer, Inline inline) {
   writer.WriteConstant(" lmo=\"");
   writer.WriteConstant((inline.SourcePosition).ToString(CultureInfo.InvariantCulture));
   writer.WriteConstant("\" lmc=\"");
   writer.WriteConstant((inline.SourceLastPosition).ToString(CultureInfo.InvariantCulture));
   writer.WriteConstant("\"");
 }
示例#11
0
 internal static void PrintPosition(HtmlTextWriter writer, Block block) {
   //LM
   writer.WriteConstant(" lmo=\"");
   writer.WriteConstant((block.SourcePosition).ToString(CultureInfo.InvariantCulture));
   writer.WriteConstant("\" lmc=\"");
   writer.WriteConstant((block.SourceLastPosition).ToString(CultureInfo.InvariantCulture));
   writer.WriteConstant("\"");
 }
示例#12
0
        private void WriteGitHubHeadingIds(HtmlTextWriter writer, Inline inline, CommonMarkSettings settings, Stack<InlineStackEntry> stack) {
            using (var tempWriter = new System.IO.StringWriter()) {
                var tempWrapper = new HtmlTextWriter(tempWriter);
                var tempStringBuilder = new StringBuilder();
                InlinesToPlainText(tempWrapper, inline, stack);
                string plaintextContent = tempWriter.ToString();

                // Normalize plaintext content according to GitHub ID rules
                plaintextContent = new Regex(@"[^\w\-\ ]").Replace(plaintextContent, "").Replace(" ", "-");
                for (int c = 0; c < plaintextContent.Length; c++) {
                    if (plaintextContent[c] >= 'A' && plaintextContent[c] <= 'Z')
                        tempStringBuilder.Append((char)(plaintextContent[c] + 32));
                    else
                        tempStringBuilder.Append(plaintextContent[c]);
                }
                plaintextContent = tempStringBuilder.ToString();
                string unique = "";
                if (GitHubHeaderIdCounts.ContainsKey(plaintextContent))
                    unique = "-" + ++GitHubHeaderIdCounts[plaintextContent];
                else
                    GitHubHeaderIdCounts[plaintextContent] = 0;

                writer.WriteConstant(string.Format(@"<a id=""user-content-{0}{1}"" class=""anchor"" href=""#user-content-{0}{1}"" aria-hidden=""true""><svg class=""octicon octicon-link"" aria-hidden=""true"" height=""16"" role=""img"" version=""1.1"" viewBox=""0 0 16 16"" width=""16""><path d=""M4 9h1v1h-1c-1.5 0-3-1.69-3-3.5s1.55-3.5 3-3.5h4c1.45 0 3 1.69 3 3.5 0 1.41-0.91 2.72-2 3.25v-1.16c0.58-0.45 1-1.27 1-2.09 0-1.28-1.02-2.5-2-2.5H4c-0.98 0-2 1.22-2 2.5s1 2.5 2 2.5z m9-3h-1v1h1c1 0 2 1.22 2 2.5s-1.02 2.5-2 2.5H9c-0.98 0-2-1.22-2-2.5 0-0.83 0.42-1.64 1-2.09v-1.16c-1.09 0.53-2 1.84-2 3.25 0 1.81 1.55 3.5 3 3.5h4c1.45 0 3-1.69 3-3.5s-1.5-3.5-3-3.5z""></path></svg></a>",
                    plaintextContent, unique));
            }
        }