Skip to content

Commit

Permalink
Extend support of nested list for non-W3C compliant html #173
Browse files Browse the repository at this point in the history
  • Loading branch information
onizet committed Nov 11, 2024
1 parent bdb076d commit 07fae6d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Support deprecrated align attribute for block #171
- Fix parsing of style attribute with a key with no value
- Improve parsing of style attribute to avoid an extra call to HtmlDecode
- Extend support of nested list for non-W3C compliant html #173

## 3.2.1

Expand Down
13 changes: 12 additions & 1 deletion src/Html2OpenXml/Expressions/Numbering/ListExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,20 @@ readonly struct ListContext(string listName, int absNumId, int instanceId, int l

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

// W3C requires that nested list stands below a `li` element but some editors
// don't care to respect the standard. Let's reparent those lists
var nestedList = node.Children.Where(n =>
n.LocalName.Equals("ol", StringComparison.OrdinalIgnoreCase) ||
n.LocalName.Equals("ul", StringComparison.OrdinalIgnoreCase));
if (nestedList.Any())
{
foreach (var list in nestedList)
list.PreviousElementSibling?.AppendChild(list);
}

var listContext = context.Properties<ListContext>("listContext");
var parentContext = listContext;
var listStyle = GetListName(node, listContext.Name);
Expand Down
42 changes: 41 additions & 1 deletion test/HtmlToOpenXml.Tests/NumberingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ public void WithRtl_ReturnsBidi(string dir, bool? expectedValue)
}

[Test]
public void NestedNumberList_ReturnsIncrementalIdentation()
public void NestedNumberList_ReturnsIncrementalIndentation()
{
const int maxLevel = 8;
var sb = new System.Text.StringBuilder();
Expand All @@ -541,5 +541,45 @@ public void NestedNumberList_ReturnsIncrementalIdentation()
TestContext.Out.WriteLine($"{i}. {ident?.Left?.Value}");
}
}

[Test(Description = "Nested list must be a children of a `li` tag but some editor are not respecting the W3C standard (issue #173)")]
public async Task NestedNumberList_NonCompliant_ReturnsIncrementalIndentation()
{
await converter.ParseBody(@"<ol>
<li>Item1</li>
<li>Item2</li>
<ol><li>Item 2.1</li></ol>
</ol>");

var absNum = mainPart.NumberingDefinitionsPart?.Numbering
.Elements<AbstractNum>()
.SingleOrDefault();
Assert.That(absNum, Is.Not.Null);

var inst = mainPart.NumberingDefinitionsPart?.Numbering
.Elements<NumberingInstance>().Where(i => i.AbstractNumId?.Val == absNum.AbstractNumberId)
.SingleOrDefault();
Assert.That(inst, Is.Not.Null);
Assert.That(inst.NumberID?.Value, Is.Not.Null);

var elements = mainPart.Document.Body!.ChildElements;
Assert.Multiple(() => {
Assert.That(elements, Has.Count.EqualTo(3));
Assert.That(elements, Is.All.TypeOf<Paragraph>());
Assert.That(mainPart.NumberingDefinitionsPart?.Numbering, Is.Not.Null);
});

// assert paragraphs linked to numbering instance
Assert.Multiple(() =>
{
Assert.That(elements.Cast<Paragraph>().Select(e =>
e.ParagraphProperties?.NumberingProperties?.NumberingId?.Val?.Value),
Has.All.EqualTo(inst.NumberID.Value),
"All paragraphs are linked to the same list instance");
Assert.That(elements.Take(2).Select(p => p.GetFirstChild<ParagraphProperties>()?.NumberingProperties?.NumberingLevelReference?.Val?.Value), Has.All.EqualTo(0));
Assert.That(elements.Last().GetFirstChild<ParagraphProperties>()?.NumberingProperties?.NumberingLevelReference?.Val?.Value, Is.EqualTo(1));
});
AssertThatOpenXmlDocumentIsValid();
}
}
}

2 comments on commit 07fae6d

@lofcz
Copy link

@lofcz lofcz commented on 07fae6d Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great addition, thanks!

@plmarzi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Please sign in to comment.