Skip to content

Commit fed20de

Browse files
committed
Handle standalone paragraphs nested in list that must be aligned with other list items #177
1 parent 07fae6d commit fed20de

File tree

2 files changed

+62
-11
lines changed

2 files changed

+62
-11
lines changed

src/Html2OpenXml/Expressions/Numbering/ListExpression.cs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,38 @@ public override IEnumerable<OpenXmlElement> Interpret(ParsingContext context)
9090
var expression = new BlockElementExpression(liNode);
9191
var childElements = expression.Interpret(context);
9292
if (!childElements.Any()) continue;
93-
Paragraph p = (Paragraph) childElements.First();
94-
95-
p.ParagraphProperties ??= new();
96-
p.ParagraphProperties.ParagraphStyleId = GetStyleIdForListItem(context.DocumentStyle, liNode);
97-
p.ParagraphProperties.NumberingProperties = new NumberingProperties {
98-
NumberingLevelReference = new() { Val = level - 1 },
99-
NumberingId = new() { Val = listContext.InstanceId }
100-
};
101-
if (listContext.Dir.HasValue) {
102-
p.ParagraphProperties.BiDi = new() {
103-
Val = OnOffValue.FromBoolean(listContext.Dir == DirectionMode.Rtl)
93+
94+
// ensure to filter out any non-paragraph like any nested table
95+
var paragraphs = childElements.OfType<Paragraph>();
96+
var listItemStyleId = GetStyleIdForListItem(context.DocumentStyle, liNode);
97+
98+
if (paragraphs.Any())
99+
{
100+
var p = paragraphs.First();
101+
p.ParagraphProperties ??= new();
102+
p.ParagraphProperties.ParagraphStyleId = listItemStyleId;
103+
p.ParagraphProperties!.NumberingProperties ??= new NumberingProperties {
104+
NumberingLevelReference = new() { Val = level - 1 },
105+
NumberingId = new() { Val = listContext.InstanceId }
106+
};
107+
if (listContext.Dir.HasValue) {
108+
p.ParagraphProperties.BiDi = new() {
109+
Val = OnOffValue.FromBoolean(listContext.Dir == DirectionMode.Rtl)
110+
};
111+
}
112+
}
113+
114+
// any standalone paragraphs must be aligned (indented) along its current level
115+
foreach (var p in paragraphs.Skip(1))
116+
{
117+
// if this is a list item paragraph, skip it
118+
if (p.ParagraphProperties?.NumberingProperties is not null)
119+
continue;
120+
121+
p.ParagraphProperties ??= new();
122+
p.ParagraphProperties.ParagraphStyleId ??= (ParagraphStyleId?) listItemStyleId!.CloneNode(true);
123+
p.ParagraphProperties.Indentation = new() {
124+
Left = (level * Indentation * 2).ToString()
104125
};
105126
}
106127

test/HtmlToOpenXml.Tests/NumberingTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,5 +581,35 @@ await converter.ParseBody(@"<ol>
581581
});
582582
AssertThatOpenXmlDocumentIsValid();
583583
}
584+
585+
[Test]
586+
public void NestedParagraph_ReturnsIndentedItems()
587+
{
588+
var elements = converter.Parse(@"<ul>
589+
<li>
590+
<p>Paragraph text</p>
591+
<p>Paragraph text</p>
592+
</li>
593+
</ul>");
594+
595+
Assert.That(elements, Is.Not.Empty);
596+
597+
var inst = mainPart.NumberingDefinitionsPart?.Numbering
598+
.Elements<NumberingInstance>()
599+
.SingleOrDefault();
600+
Assert.That(inst, Is.Not.Null);
601+
Assert.Multiple(() => {
602+
Assert.That(elements.Last().GetFirstChild<ParagraphProperties>()?.NumberingProperties?.NumberingId,
603+
Is.Null,
604+
"Last paragraph is standalone and not linked to a list instance");
605+
Assert.That(elements.Cast<Paragraph>().Select(e =>
606+
e.ParagraphProperties?.ParagraphStyleId?.Val?.Value),
607+
Has.All.EqualTo("ListParagraph"),
608+
"All paragraphs use the same paragraph style");
609+
Assert.That(elements.Last().GetFirstChild<ParagraphProperties>()?.Indentation?.Left?.Value,
610+
Is.EqualTo("720"),
611+
"Last standalone paragraph is aligned with the level 1");
612+
});
613+
}
584614
}
585615
}

0 commit comments

Comments
 (0)