Wp2ghost/CSharpGhostImporter/HtmlToMarkdownConverter.cs
2026-09-20 08:31:23 +02:00

143 lines
No EOL
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace wp2ghost
{
// =============================================================================
// HTML zu Markdown Konverter
// =============================================================================
public static class HtmlToMarkdownConverter
{
public static string Convert(string html)
{
if (string.IsNullOrEmpty(html))
return "";
string result = html;
result = System.Text.RegularExpressions.Regex.Replace(result, "<!--.*?-->", "", System.Text.RegularExpressions.RegexOptions.Singleline);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<h([1-6])[^>]*>(.*?)</h\1>", m => new string('#', int.Parse(m.Groups[1].Value)) + " " + StripTags(m.Groups[2].Value) + Environment.NewLine + Environment.NewLine, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<blockquote[^>]*>(.*?)</blockquote>", m => "> " + StripTags(m.Groups[1].Value).Replace(Environment.NewLine, Environment.NewLine + "> ") + Environment.NewLine, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<pre[^>]*><code[^>]*>(.*?)</code></pre>", m => "```\n" + StripTags(m.Groups[1].Value) + "\n```\n\n", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<code[^>]*>(.*?)</code>", m => "`" + StripTags(m.Groups[1].Value) + "`", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<ul[^>]*>(.*?)</ul>", m => ConvertList(m.Groups[1].Value, "-"), System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<ol[^>]*>(.*?)</ol>", m => ConvertOrderedList(m.Groups[1].Value), System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = ConvertTables(result);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<p[^>]*>(.*?)</p>", m => CleanInnerHtml(m.Groups[1].Value) + Environment.NewLine + Environment.NewLine, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<br\s*/?>", Environment.NewLine, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<strong[^>]*>(.*?)</strong>", "**$1**", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<b[^>]*>(.*?)</b>", "**$1**", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<em[^>]*>(.*?)</em>", "*$1*", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<i[^>]*>(.*?)</i>", "*$1*", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<a\s+[^>]*href=[""']([^""']+)[""'][^>]*>(.*?)</a>", "[$2]($1)", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<img\s+[^>]*src=[""']([^""']+)[""'][^>]*alt=[""']([^""']*)[""'][^>]*>", m => "![" + m.Groups[2].Value + "](" + m.Groups[1].Value + ")", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<img\s+[^>]*src=[""']([^""']+)[""'][^>]*>", m => "![](" + m.Groups[1].Value + ")", System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result, @"<div[^>]*class=[""'][^""']*wp-block-heading[^""']*[""'][^>]*>(.*?)</div>", m => "# " + StripTags(m.Groups[1].Value) + Environment.NewLine + Environment.NewLine, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = System.Text.RegularExpressions.Regex.Replace(result,
@"<figure[^>]*>(.*?)</figure>",
m => ConvertFigure(m.Groups[1].Value),
System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
result = StripTagsKeepFigure(result);
result = System.Text.RegularExpressions.Regex.Replace(result, @"\n{3,}", "\n\n");
result = result.Replace("&amp;", "&").Replace("&lt;", "<").Replace("&gt;", ">").Replace("&quot;", "\"").Replace("&#8217;", "'").Replace("&nbsp;", " ").Replace("&mdash;", "—").Replace("&ndash;", "–");
return result.Trim();
}
private static string StripTags(string html) =>
System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", "");
private static string CleanInnerHtml(string html)
{
string cleaned = StripTags(html);
cleaned = System.Text.RegularExpressions.Regex.Replace(cleaned, @"\*\*(.+?)\*\*", "**$1**");
return cleaned.Trim();
}
private static string ConvertFigure(string figureContent)
{
string imgSrc = "", altText = "", caption = "";
var imgMatch = System.Text.RegularExpressions.Regex.Match(figureContent, @"<img\s+[^>]*src=[""']([^""']+)[""']");
if (imgMatch.Success) imgSrc = imgMatch.Groups[1].Value;
var altMatch = System.Text.RegularExpressions.Regex.Match(figureContent, @"alt=[""']([^""']*)[""']");
if (altMatch.Success) altText = altMatch.Groups[1].Value;
var figMatch = System.Text.RegularExpressions.Regex.Match(figureContent, @"<figcaption[^>]*>(.*?)</figcaption>", System.Text.RegularExpressions.RegexOptions.Singleline);
if (figMatch.Success) caption = System.Text.RegularExpressions.Regex.Replace(figMatch.Groups[1].Value, "<[^>]*>", "").Trim();
if (string.IsNullOrEmpty(imgSrc)) return "";
string result = $"<figure class=\"kg-width-wide kg-card-width-wide\"><img src=\"{imgSrc}\" loading=\"lazy\"";
if (!string.IsNullOrEmpty(altText)) result += $" alt=\"{altText}\"";
result += ">";
if (!string.IsNullOrEmpty(caption)) result += $"<figcaption>{caption}</figcaption>";
result += "</figure>";
return result + Environment.NewLine;
}
private static string StripTagsKeepFigure(string html)
{
var figureBlocks = new List<string>();
html = System.Text.RegularExpressions.Regex.Replace(html, @"<figure[^>]*>.*?</figure>", m => { figureBlocks.Add(m.Value); return $"__FIGURE_{figureBlocks.Count - 1}__"; }, System.Text.RegularExpressions.RegexOptions.Singleline);
html = System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", "");
for (int i = 0; i < figureBlocks.Count; i++)
html = html.Replace($"__FIGURE_{i}__", figureBlocks[i]);
return html;
}
private static string ConvertList(string listHtml, string bullet)
{
listHtml = System.Text.RegularExpressions.Regex.Replace(listHtml, @"<li[^>]*>(.*?)</li>", m => bullet + " " + CleanInnerHtml(m.Groups[1].Value) + Environment.NewLine, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
return StripTags(listHtml).Trim() + Environment.NewLine;
}
private static string ConvertOrderedList(string listHtml)
{
int counter = 0;
listHtml = System.Text.RegularExpressions.Regex.Replace(listHtml, @"<li[^>]*>(.*?)</li>", m => { counter++; return counter + ". " + CleanInnerHtml(m.Groups[1].Value) + Environment.NewLine; }, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
return StripTags(listHtml).Trim() + Environment.NewLine;
}
private static string ConvertTables(string html)
{
return System.Text.RegularExpressions.Regex.Replace(html, @"<table[^>]*>(.*?)</table>", m =>
{
string tableContent = m.Groups[1].Value;
var headerMatch = System.Text.RegularExpressions.Regex.Match(tableContent, @"<tr[^>]*>(.*?)</tr>", System.Text.RegularExpressions.RegexOptions.Singleline);
if (!headerMatch.Success) return StripTags(tableContent) + Environment.NewLine;
string headers = CleanInnerHtml(headerMatch.Groups[1].Value);
headers = headers.Replace("<th", "<td").Replace("</th>", "</td>");
string headerLine = "| " + string.Join(" | ", StripTags(headers).Split(new[] { "</td>", "<td" }, StringSplitOptions.RemoveEmptyEntries).Select(c => c.Trim())) + " |";
string separator = "|" + string.Join("|", StripTags(headers).Split(new[] { "</td>", "<td" }, StringSplitOptions.RemoveEmptyEntries).Select(c => "---")) + "|";
var rows = System.Text.RegularExpressions.Regex.Matches(tableContent, @"<tr[^>]*>(.*?)</tr>", System.Text.RegularExpressions.RegexOptions.Singleline);
var dataRows = new List<string>();
for (int i = 1; i < rows.Count; i++)
{
string rowText = CleanInnerHtml(rows[i].Groups[1].Value);
string[] cells = rowText.Split(new[] { "</td>", "<td" }, StringSplitOptions.RemoveEmptyEntries);
dataRows.Add("| " + string.Join(" | ", cells.Select(c => c.Trim().Replace("<b>", "").Replace("</b>", "").Replace("<strong>", "").Replace("</strong>", ""))) + " |");
}
return headerLine + Environment.NewLine + separator + Environment.NewLine + string.Join(Environment.NewLine, dataRows) + Environment.NewLine + Environment.NewLine;
}, System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
}
}
}