{"id":13431168,"url":"https://github.com/mysticmind/reversemarkdown-net","last_synced_at":"2026-02-24T06:53:53.772Z","repository":{"id":1641613,"uuid":"38600982","full_name":"mysticmind/reversemarkdown-net","owner":"mysticmind","description":"ReverseMarkdown.Net is a Html to Markdown converter library in C#. Conversion is very reliable since HtmlAgilityPack (HAP) library is used for traversing the Html DOM","archived":false,"fork":false,"pushed_at":"2024-06-29T00:57:12.000Z","size":475,"stargazers_count":279,"open_issues_count":13,"forks_count":66,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-23T12:22:56.815Z","etag":null,"topics":["converter-library","dotnet","html","markdown","markdown-to-html","netcore","netstandard"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mysticmind.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"mysticmind"}},"created_at":"2015-07-06T06:16:38.000Z","updated_at":"2024-10-19T12:51:31.000Z","dependencies_parsed_at":"2023-12-11T18:35:52.434Z","dependency_job_id":"7cbe48fc-45d0-4957-be50-941acf1f1250","html_url":"https://github.com/mysticmind/reversemarkdown-net","commit_stats":{"total_commits":267,"total_committers":23,"mean_commits":"11.608695652173912","dds":"0.33333333333333337","last_synced_commit":"90e97bbd92e059c45e219eed373b6666e3779c1e"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mysticmind%2Freversemarkdown-net","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mysticmind%2Freversemarkdown-net/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mysticmind%2Freversemarkdown-net/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mysticmind%2Freversemarkdown-net/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mysticmind","download_url":"https://codeload.github.com/mysticmind/reversemarkdown-net/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247737788,"owners_count":20987721,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["converter-library","dotnet","html","markdown","markdown-to-html","netcore","netstandard"],"created_at":"2024-07-31T02:01:01.025Z","updated_at":"2026-01-08T18:05:29.126Z","avatar_url":"https://github.com/mysticmind.png","language":"C#","funding_links":["https://github.com/sponsors/mysticmind"],"categories":["Frameworks, Libraries and Tools","C#","框架, 库和工具","C\\#","markdown","Misc","HTML","dotnet"],"sub_categories":["Misc","大杂烩"],"readme":"# Meet ReverseMarkdown\n\n[![Build status](https://github.com/mysticmind/reversemarkdown-net/actions/workflows/ci.yaml/badge.svg)](https://github.com/mysticmind/reversemarkdown-net/actions/workflows/ci.yaml) [![NuGet Version](https://badgen.net/nuget/v/reversemarkdown)](https://www.nuget.org/packages/ReverseMarkdown/)\n\nReverseMarkdown is a Html to Markdown converter library in C#. Conversion is very reliable since the HtmlAgilityPack (HAP) library is used for traversing the HTML DOM.\n\nIf you have used and benefitted from this library. Please feel free to sponsor me!\u003cbr\u003e\n\u003ca href=\"https://github.com/sponsors/mysticmind\" target=\"_blank\"\u003e\u003cimg height=\"30\" style=\"border:0px;height:36px;\" src=\"https://img.shields.io/static/v1?label=GitHub Sponsor\u0026message=%E2%9D%A4\u0026logo=GitHub\" border=\"0\" alt=\"GitHub Sponsor\" /\u003e\u003c/a\u003e\n\n## Usage\n\nInstall the package from NuGet using `Install-Package ReverseMarkdown` or clone the repository and build it yourself.\n\n\u003c!-- snippet: Usage --\u003e\n\u003ca id='snippet-Usage'\u003e\u003c/a\u003e\n```cs\nvar converter = new ReverseMarkdown.Converter();\n\nstring html = \"This a sample \u003cstrong\u003eparagraph\u003c/strong\u003e from \u003ca href=\\\"http://test.com\\\"\u003emy site\u003c/a\u003e\";\n\nstring result = converter.Convert(html);\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.cs#L12-L20' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Usage' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nWill result in:\n\n\u003c!-- snippet: Snippets.Usage.verified.txt --\u003e\n\u003ca id='snippet-Snippets.Usage.verified.txt'\u003e\u003c/a\u003e\n```txt\nThis a sample **paragraph** from [my site](http://test.com)\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.Usage.verified.txt#L1-L1' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Snippets.Usage.verified.txt' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThe conversion can also be customized:\n\n\u003c!-- snippet: UsageWithConfig --\u003e\n\u003ca id='snippet-UsageWithConfig'\u003e\u003c/a\u003e\n```cs\nvar config = new ReverseMarkdown.Config\n{\n    // Include the unknown tag completely in the result (default as well)\n    UnknownTags = Config.UnknownTagsOption.PassThrough,\n    // generate GitHub flavoured markdown, supported for BR, PRE and table tags\n    GithubFlavored = true,\n    // will ignore all comments\n    RemoveComments = true,\n    // remove markdown output for links where appropriate\n    SmartHrefHandling = true\n};\n\nvar converter = new ReverseMarkdown.Converter(config);\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.cs#L28-L44' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-UsageWithConfig' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n## Configuration options\n\n* `DefaultCodeBlockLanguage` - Option to set the default code block language for Github style markdown if class based language markers are not available\n* `GithubFlavored` - Github style markdown for br, pre and table. Default is false\n* `SlackFlavored` - Slack style markdown formatting. When enabled, uses `*` for bold, `_` for italic, `~` for strikethrough, and `•` for list bullets. Default is false\n* `CleanupUnnecessarySpaces` - Cleanup unnecessary spaces in the output. Default is true\n* `SuppressDivNewlines` - Removes prefixed newlines from `div` tags. Default is false\n* `ListBulletChar` - Allows you to change the bullet character. Default value is `-`. Some systems expect the bullet character to be `*` rather than `-`, this config allows you to change it. Note: This option is ignored when `SlackFlavored` is enabled\n* `RemoveComments` - Remove comment tags with text. Default is false\n* `SmartHrefHandling` - How to handle `\u003ca\u003e` tag href attribute\n  * `false` - Outputs `[{name}]({href}{title})` even if the name and href is identical. This is the default option.\n  * `true` - If the name and href equals, outputs just the `name`. Note that if the Uri is not well formed as per [`Uri.IsWellFormedUriString`](https://docs.microsoft.com/en-us/dotnet/api/system.uri.iswellformeduristring) (i.e string is not correctly escaped like `http://example.com/path/file name.docx`) then markdown syntax will be used anyway.\n\n    If `href` contains `http/https` protocol, and `name` doesn't but otherwise are the same, output `href` only\n\n    If `tel:` or `mailto:` scheme, but afterwards identical with name, output `name` only.\n* `UnknownTags` - handle unknown tags.\n  * `UnknownTagsOption.PassThrough` - Include the unknown tag completely into the result. That is, the tag along with the text will be left in output. This is the default\n  * `UnknownTagsOption.Drop` - Drop the unknown tag and its content\n  * `UnknownTagsOption.Bypass` - Ignore the unknown tag but try to convert its content\n  * `UnknownTagsOption.Raise` - Raise an error to let you know\n* `PassThroughTags` - Pass a list of tags to pass through as-is without any processing.\n* `WhitelistUriSchemes` - Specify which schemes (without trailing colon) are to be allowed for `\u003ca\u003e` and `\u003cimg\u003e` tags. Others will be bypassed (output text or nothing). By default allows everything.\n\n  If `string.Empty` provided and when `href` or `src` schema couldn't be determined - whitelists\n\n  Schema is determined by `Uri` class, with exception when url begins with `/` (file schema) and `//` (http schema)\n* `TableWithoutHeaderRowHandling` - handle table without header rows\n  * `TableWithoutHeaderRowHandlingOption.Default` - First row will be used as header row (default)\n  * `TableWithoutHeaderRowHandlingOption.EmptyRow` - An empty row will be added as the header row\n* `TableHeaderColumnSpanHandling` - Set this flag to handle or process table header column with column spans. Default is true\n* `Base64Images` - Control how base64-encoded images (inline data URIs) are handled during conversion\n  * `Base64ImageHandling.Include` - Include base64-encoded images in the markdown output as-is (default behavior)\n  * `Base64ImageHandling.Skip` - Skip/ignore base64-encoded images entirely\n  * `Base64ImageHandling.SaveToFile` - Save base64-encoded images to disk and reference the saved file path in markdown. Requires `Base64ImageSaveDirectory` to be set\n* `Base64ImageSaveDirectory` - When `Base64Images` is set to `SaveToFile`, specifies the directory path where images should be saved\n* `Base64ImageFileNameGenerator` - When `Base64Images` is set to `SaveToFile`, this function generates a filename for each saved image. The function receives the image index (int) and MIME type (string), and should return a filename without extension. If not specified, images will be named as `image_0`, `image_1`, etc.\n\n### Base64 Image Handling Examples\n\nReverseMarkdown provides flexible options for handling base64-encoded images (inline data URIs) during HTML to Markdown conversion.\n\n**Include Base64 Images (Default)**\n\nBy default, base64-encoded images are included in the markdown output as-is:\n\n\u003c!-- snippet: Base64ImageInclude --\u003e\n\u003ca id='snippet-Base64ImageInclude'\u003e\u003c/a\u003e\n```cs\nvar converter = new ReverseMarkdown.Converter();\nstring html = \"\u003cimg src=\\\"data:image/png;base64,iVBORw0KGg...\\\" alt=\\\"Sample Image\\\"/\u003e\";\nstring result = converter.Convert(html);\n// Output: ![Sample Image](data:image/png;base64,iVBORw0KGg...)\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.cs#L50-L57' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Base64ImageInclude' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n**Skip Base64 Images**\n\nTo ignore base64-encoded images entirely:\n\n\u003c!-- snippet: Base64ImageSkip --\u003e\n\u003ca id='snippet-Base64ImageSkip'\u003e\u003c/a\u003e\n```cs\nvar config = new ReverseMarkdown.Config\n{\n    Base64Images = Config.Base64ImageHandling.Skip\n};\nvar converter = new ReverseMarkdown.Converter(config);\nstring html = \"\u003cimg src=\\\"data:image/png;base64,iVBORw0KGg...\\\" alt=\\\"Sample Image\\\"/\u003e\";\nstring result = converter.Convert(html);\n// Output: (empty - image is skipped)\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.cs#L63-L74' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Base64ImageSkip' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n**Save Base64 Images to Disk**\n\nTo extract and save base64-encoded images to disk:\n\n\u003c!-- snippet: Base64ImageSaveToFile --\u003e\n\u003ca id='snippet-Base64ImageSaveToFile'\u003e\u003c/a\u003e\n```cs\nvar config = new ReverseMarkdown.Config\n{\n    Base64Images = Config.Base64ImageHandling.SaveToFile,\n    Base64ImageSaveDirectory = \"/path/to/images\"\n};\nvar converter = new ReverseMarkdown.Converter(config);\nstring html = \"\u003cimg src=\\\"data:image/png;base64,iVBORw0KGg...\\\" alt=\\\"Sample Image\\\"/\u003e\";\nstring result = converter.Convert(html);\n// Output: ![Sample Image](/path/to/images/image_0.png)\n// Image file saved to: /path/to/images/image_0.png\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.cs#L80-L93' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Base64ImageSaveToFile' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n**Custom Filename Generator**\n\nYou can provide a custom filename generator for saved images:\n\n\u003c!-- snippet: Base64ImageCustomFilename --\u003e\n\u003ca id='snippet-Base64ImageCustomFilename'\u003e\u003c/a\u003e\n```cs\nvar config = new ReverseMarkdown.Config\n{\n    Base64Images = Config.Base64ImageHandling.SaveToFile,\n    Base64ImageSaveDirectory = \"/path/to/images\",\n    Base64ImageFileNameGenerator = (index, mimeType) =\u003e \n    {\n        var timestamp = DateTime.Now.ToString(\"yyyyMMdd_HHmmss\");\n        return $\"converted_{timestamp}_{index}\";\n    }\n};\nvar converter = new ReverseMarkdown.Converter(config);\n// Images will be saved as: converted_20260108_143022_0.png, converted_20260108_143022_1.jpg, etc.\n```\n\u003csup\u003e\u003ca href='/src/ReverseMarkdown.Test/Snippets.cs#L99-L114' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Base64ImageCustomFilename' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n**Supported Image Formats:**\n- PNG (`image/png`)\n- JPEG (`image/jpeg`, `image/jpg`)\n- GIF (`image/gif`)\n- BMP (`image/bmp`)\n- TIFF (`image/tiff`)\n- WebP (`image/webp`)\n- SVG (`image/svg+xml`)\n\n## Features\n\n* Supports all the established html tags like h1, h2, h3, h4, h5, h6, p, em, strong, i, b, blockquote, code, img, a, hr, li, ol, ul, table, tr, th, td, br\n* Supports nested lists\n* Github Flavoured Markdown conversion supported for br, pre, tasklists and table. Use `var config = new ReverseMarkdown.Config(githubFlavoured:true);`. By default the table will always be converted to Github flavored markdown immaterial of this flag\n* Slack Flavoured Markdown conversion supported. Use `var config = new ReverseMarkdown.Config { SlackFlavored = true };`\n* Improved performance with optimized text writer approach and O(1) ancestor lookups\n* Support for nested tables (converted as HTML inside markdown)\n* Support for table captions (rendered as paragraph above table)\n* Base64-encoded image handling with options to include as-is, skip, or save to disk\n\n## Breaking Changes\n\n### v5.0.0\n\n**Configuration Changes:**\n* `WhitelistUriSchemes` - Changed from `string[]` to `HashSet\u003cstring\u003e` (read-only property). Use `.Add()` method to add schemes instead of array assignment\n* `PassThroughTags` - Changed from `string[]` to `HashSet\u003cstring\u003e`\n\n**API Changes:**\n* `IConverter` interface signature changed from `string Convert(HtmlNode node)` to `void Convert(TextWriter writer, HtmlNode node)`. If you have custom converters, you'll need to update them to write to the TextWriter instead of returning a string\n\n**Target Framework Changes:**\n\n* Removed support for legacy and end-of-life .NET versions. Only actively supported .NET versions are now targeted i.e. .NET 8, .NET 9 and .NET 10.\n\n### v2.0.0\n\n* `UnknownTags` config has been changed to an enumeration\n\n## Acknowledgements\n\nThis library's initial implementation ideas from the Ruby based Html to Markdown converter [xijo/reverse_markdown](https://github.com/xijo/reverse_markdown).\n\n## Copyright\n\nCopyright © Babu Annamalai\n\n## License\n\nReverseMarkdown is licensed under [MIT](http://www.opensource.org/licenses/mit-license.php \"Read more about the MIT license form\"). Refer to [License file](https://github.com/mysticmind/reversemarkdown-net/blob/master/LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmysticmind%2Freversemarkdown-net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmysticmind%2Freversemarkdown-net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmysticmind%2Freversemarkdown-net/lists"}