-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from whistyun/feature/plugin
html support
- Loading branch information
Showing
80 changed files
with
3,670 additions
and
123 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
44 changes: 44 additions & 0 deletions
44
MdXaml.Html/Core/Parsers.MarkdigExtensions/FigureParser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
211
MdXaml.Html/Core/Parsers.MarkdigExtensions/GridTableParser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.