Skip to content

Commit

Permalink
Merge pull request #40 from whistyun/feature/plugin
Browse files Browse the repository at this point in the history
html support
  • Loading branch information
whistyun authored Oct 2, 2022
2 parents 1d6ca7c + e1b5ab2 commit e50afc2
Show file tree
Hide file tree
Showing 80 changed files with 3,670 additions and 123 deletions.
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

44 changes: 44 additions & 0 deletions MdXaml.Html/Core/Parsers.MarkdigExtensions/FigureParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using HtmlAgilityPack;
using MdXaml.Html.Core.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;

namespace MdXaml.Html.Core.Parsers.MarkdigExtensions
{
public class FigureParser : IBlockTagParser
{
public IEnumerable<string> SupportTag => new[] { "figure" };

bool ITagParser.TryReplace(HtmlNode node, ReplaceManager manager, out IEnumerable<TextElement> generated)
{
var rtn = TryReplace(node, manager, out var list);
generated = list;
return rtn;
}

public bool TryReplace(HtmlNode node, ReplaceManager manager, out IEnumerable<Block> generated)
{
var captionPair =
node.ChildNodes
.SkipComment()
.Filter(nd => string.Equals(nd.Name, "figcaption", StringComparison.OrdinalIgnoreCase));

var captionList = captionPair.Item1;
var contentList = captionPair.Item2;


var captionBlock = captionList.SelectMany(c => manager.Grouping(manager.ParseBlockAndInline(c)));
var contentBlock = contentList.SelectMany(c => manager.Grouping(manager.ParseChildrenJagging(c)));

var section = new Section();
section.Tag = manager.GetTag(Tags.TagFigure);
section.Blocks.AddRange(contentBlock);
section.Blocks.AddRange(captionBlock);

generated = new[] { section };
return false;
}
}
}
211 changes: 211 additions & 0 deletions MdXaml.Html/Core/Parsers.MarkdigExtensions/GridTableParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Documents;
using System.Windows;
using MdXaml.Html.Core.Utils;

namespace MdXaml.Html.Core.Parsers.MarkdigExtensions
{
public class GridTableParser : IBlockTagParser, IHasPriority
{
public int Priority => HasPriority.DefaultPriority + 1000;

public IEnumerable<string> SupportTag => new[] { "table" };

bool ITagParser.TryReplace(HtmlNode node, ReplaceManager manager, out IEnumerable<TextElement> generated)
{
var rtn = TryReplace(node, manager, out var list);
generated = list;
return rtn;
}

public bool TryReplace(HtmlNode node, ReplaceManager manager, out IEnumerable<Block> generated)
{
var table = new Table();

ParseColumnStyle(node, table);

int totalColCount = 0;

var theadRows = node.SelectNodes("./thead/tr");
if (theadRows is not null)
{
var group = CreateRowGroup(theadRows, manager, out int colCount);
group.Tag = manager.GetTag(Tags.TagTableHeader);
table.RowGroups.Add(group);

totalColCount = Math.Max(totalColCount, colCount);
}

var tbodyRows = new List<HtmlNode>();
foreach (var child in node.ChildNodes)
{
if (string.Equals(child.Name, "tr", StringComparison.OrdinalIgnoreCase))
tbodyRows.Add(child);

if (string.Equals(child.Name, "tbody", StringComparison.OrdinalIgnoreCase))
tbodyRows.AddRange(child.ChildNodes.CollectTag("tr"));
}
if (tbodyRows.Count > 0)
{
var group = CreateRowGroup(tbodyRows, manager, out int colCount);
group.Tag = manager.GetTag(Tags.TagTableBody);
table.RowGroups.Add(group);

int idx = 0;
foreach (var row in group.Rows)
{
var useTag = (++idx & 1) == 0 ? Tags.TagEvenTableRow : Tags.TagOddTableRow;
row.Tag = manager.GetTag(useTag);
}

totalColCount = Math.Max(totalColCount, colCount);
}

var tfootRows = node.SelectNodes("./tfoot/tr");
if (tfootRows is not null)
{
var group = CreateRowGroup(tfootRows, manager, out int colCount);
group.Tag = manager.GetTag(Tags.TagTableFooter);
table.RowGroups.Add(group);

totalColCount = Math.Max(totalColCount, colCount);
}

while (totalColCount >= table.Columns.Count)
{
table.Columns.Add(new TableColumn());
}

var captions = node.SelectNodes("./caption");
if (captions is not null)
{
var tableSec = new Section();
foreach (var cap in captions)
{
tableSec.Blocks.AddRange(manager.ParseChildrenAndGroup(cap));
}

tableSec.Blocks.Add(table);
tableSec.Tag = manager.GetTag(Tags.TagTableCaption);

generated = new[] { tableSec };
}
else
{
generated = new[] { table };
}

return true;
}

private static void ParseColumnStyle(HtmlNode tableTag, Table table)
{
var colHolder = tableTag.ChildNodes.HasOneTag("colgroup", out var colgroup) ? colgroup! : tableTag;

foreach (var col in colHolder.ChildNodes.CollectTag("col"))
{
var coldef = new TableColumn();
table.Columns.Add(coldef);

var spanAttr = col.Attributes["span"];
if (spanAttr is not null)
{
if (int.TryParse(spanAttr.Value, out var spanCnt))
{
foreach (var _ in Enumerable.Range(0, spanCnt - 1))
table.Columns.Add(coldef);
}
}

var styleAttr = col.Attributes["style"];
if (styleAttr is null) continue;

var mch = Regex.Match(styleAttr.Value, "width:([^;\"]+)(%|em|ex|mm|cm|in|pt|pc|)");
if (!mch.Success) continue;

if (!Length.TryParse(mch.Groups[1].Value + mch.Groups[2].Value, out var length))
continue;

coldef.Width = length.Unit switch
{
Unit.Percentage => new GridLength(length.Value, GridUnitType.Star),
_ => new GridLength(length.ToPoint())
};
}
}


private static TableRowGroup CreateRowGroup(
IEnumerable<HtmlNode> rows,
ReplaceManager manager,
out int maxColCount)
{
var group = new TableRowGroup();
var list = new List<ColspanCounter>();

maxColCount = 0;

foreach (var rowTag in rows)
{
var row = new TableRow();

int colCount = list.Sum(e => e.ColSpan);

foreach (var cellTag in rowTag.ChildNodes.CollectTag("td", "th"))
{
var cell = new TableCell();
cell.Blocks.AddRange(manager.ParseChildrenAndGroup(cellTag));

int colspan = TryParse(cellTag.Attributes["colspan"]?.Value);
int rowspan = TryParse(cellTag.Attributes["rowspan"]?.Value);

cell.RowSpan = rowspan;
cell.ColumnSpan = colspan;

row.Cells.Add(cell);

colCount += colspan;

if (rowspan > 1)
{
list.Add(new ColspanCounter(rowspan, colspan));
}
}

group.Rows.Add(row);

maxColCount = Math.Max(maxColCount, colCount);

for (int idx = list.Count - 1; idx >= 0; --idx)
if (list[idx].Detent())
list.RemoveAt(idx);
}

return group;

static int TryParse(string? txt) => int.TryParse(txt, out var v) ? v : 1;
}


class ColspanCounter
{
public int Remain { get; set; }
public int ColSpan { get; }

public ColspanCounter(int rowspan, int colspan)
{
Remain = rowspan;
ColSpan = colspan;
}

public bool Detent()
{
return --Remain == 0;
}
}
}
}
88 changes: 88 additions & 0 deletions MdXaml.Html/Core/Parsers/ButtonParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace MdXaml.Html.Core.Parsers
{
public class ButtonParser : IInlineTagParser
{
public IEnumerable<string> SupportTag => new[] { "button" };

bool ITagParser.TryReplace(HtmlNode node, ReplaceManager manager, out IEnumerable<TextElement> generated)
{
var rtn = TryReplace(node, manager, out var list);
generated = list;
return rtn;
}

public bool TryReplace(HtmlNode node, ReplaceManager manager, out IEnumerable<Inline> generated)
{
var doc = new FlowDocument();
doc.Blocks.AddRange(manager.ParseChildrenAndGroup(node));

var box = new FlowDocumentScrollViewer()
{
Margin = new Thickness(0),
Padding = new Thickness(0),
VerticalScrollBarVisibility = ScrollBarVisibility.Disabled,
HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
Document = doc,
};

box.Loaded += (s, e) =>
{
var desiredWidth = box.DesiredSize.Width;
var desiredHeight = box.DesiredSize.Height;


for (int i = 0; i < 10; ++i)
{
desiredWidth /= 2;
var size = new Size(desiredWidth, double.PositiveInfinity);

box.Measure(size);

if (desiredHeight != box.DesiredSize.Height) break;

// Give up because it will not be wrapped back.
if (i == 9) return;
}

var preferedWidth = desiredWidth * 2;

for (int i = 0; i < 10; ++i)
{
var width = (desiredWidth + preferedWidth) / 2;

var size = new Size(width, double.PositiveInfinity);
box.Measure(size);

if (desiredHeight == box.DesiredSize.Height)
{
preferedWidth = width;
}
else
{
desiredWidth = width;
}
}

box.Width = preferedWidth;
};


var btn = new Button()
{
Content = box,
IsEnabled = false,
};

generated = new[] { new InlineUIContainer(btn) };
return true;
}
}
}
Loading

0 comments on commit e50afc2

Please sign in to comment.