Skip to content

Commit 07fae6d

Browse files
committed
Extend support of nested list for non-W3C compliant html #173
1 parent bdb076d commit 07fae6d

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Support deprecrated align attribute for block #171
88
- Fix parsing of style attribute with a key with no value
99
- Improve parsing of style attribute to avoid an extra call to HtmlDecode
10+
- Extend support of nested list for non-W3C compliant html #173
1011

1112
## 3.2.1
1213

src/Html2OpenXml/Expressions/Numbering/ListExpression.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,20 @@ readonly struct ListContext(string listName, int absNumId, int instanceId, int l
5050

5151
public override IEnumerable<OpenXmlElement> Interpret(ParsingContext context)
5252
{
53-
var liNodes = node.Children.Where(n => n.LocalName == "li");
53+
var liNodes = node.Children.Where(n => n.LocalName.Equals("li", StringComparison.OrdinalIgnoreCase));
5454
if (!liNodes.Any()) yield break;
5555

56+
// W3C requires that nested list stands below a `li` element but some editors
57+
// don't care to respect the standard. Let's reparent those lists
58+
var nestedList = node.Children.Where(n =>
59+
n.LocalName.Equals("ol", StringComparison.OrdinalIgnoreCase) ||
60+
n.LocalName.Equals("ul", StringComparison.OrdinalIgnoreCase));
61+
if (nestedList.Any())
62+
{
63+
foreach (var list in nestedList)
64+
list.PreviousElementSibling?.AppendChild(list);
65+
}
66+
5667
var listContext = context.Properties<ListContext>("listContext");
5768
var parentContext = listContext;
5869
var listStyle = GetListName(node, listContext.Name);

test/HtmlToOpenXml.Tests/NumberingTests.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ public void WithRtl_ReturnsBidi(string dir, bool? expectedValue)
514514
}
515515

516516
[Test]
517-
public void NestedNumberList_ReturnsIncrementalIdentation()
517+
public void NestedNumberList_ReturnsIncrementalIndentation()
518518
{
519519
const int maxLevel = 8;
520520
var sb = new System.Text.StringBuilder();
@@ -541,5 +541,45 @@ public void NestedNumberList_ReturnsIncrementalIdentation()
541541
TestContext.Out.WriteLine($"{i}. {ident?.Left?.Value}");
542542
}
543543
}
544+
545+
[Test(Description = "Nested list must be a children of a `li` tag but some editor are not respecting the W3C standard (issue #173)")]
546+
public async Task NestedNumberList_NonCompliant_ReturnsIncrementalIndentation()
547+
{
548+
await converter.ParseBody(@"<ol>
549+
<li>Item1</li>
550+
<li>Item2</li>
551+
<ol><li>Item 2.1</li></ol>
552+
</ol>");
553+
554+
var absNum = mainPart.NumberingDefinitionsPart?.Numbering
555+
.Elements<AbstractNum>()
556+
.SingleOrDefault();
557+
Assert.That(absNum, Is.Not.Null);
558+
559+
var inst = mainPart.NumberingDefinitionsPart?.Numbering
560+
.Elements<NumberingInstance>().Where(i => i.AbstractNumId?.Val == absNum.AbstractNumberId)
561+
.SingleOrDefault();
562+
Assert.That(inst, Is.Not.Null);
563+
Assert.That(inst.NumberID?.Value, Is.Not.Null);
564+
565+
var elements = mainPart.Document.Body!.ChildElements;
566+
Assert.Multiple(() => {
567+
Assert.That(elements, Has.Count.EqualTo(3));
568+
Assert.That(elements, Is.All.TypeOf<Paragraph>());
569+
Assert.That(mainPart.NumberingDefinitionsPart?.Numbering, Is.Not.Null);
570+
});
571+
572+
// assert paragraphs linked to numbering instance
573+
Assert.Multiple(() =>
574+
{
575+
Assert.That(elements.Cast<Paragraph>().Select(e =>
576+
e.ParagraphProperties?.NumberingProperties?.NumberingId?.Val?.Value),
577+
Has.All.EqualTo(inst.NumberID.Value),
578+
"All paragraphs are linked to the same list instance");
579+
Assert.That(elements.Take(2).Select(p => p.GetFirstChild<ParagraphProperties>()?.NumberingProperties?.NumberingLevelReference?.Val?.Value), Has.All.EqualTo(0));
580+
Assert.That(elements.Last().GetFirstChild<ParagraphProperties>()?.NumberingProperties?.NumberingLevelReference?.Val?.Value, Is.EqualTo(1));
581+
});
582+
AssertThatOpenXmlDocumentIsValid();
583+
}
544584
}
545585
}

0 commit comments

Comments
 (0)