diff --git a/src/Html2OpenXml/Expressions/BlockElementExpression.cs b/src/Html2OpenXml/Expressions/BlockElementExpression.cs index e15db50..9d4ed3d 100644 --- a/src/Html2OpenXml/Expressions/BlockElementExpression.cs +++ b/src/Html2OpenXml/Expressions/BlockElementExpression.cs @@ -23,11 +23,21 @@ namespace HtmlToOpenXml.Expressions; /// Process the parsing of block contents (like p, span, heading). /// A block-level element always starts on a new line, and the browsers automatically add some space (a margin) before and after the element. /// -class BlockElementExpression(IHtmlElement node, params OpenXmlLeafElement[]? styleProperty) : PhrasingElementExpression(node) +class BlockElementExpression: PhrasingElementExpression { - private readonly OpenXmlLeafElement[]? defaultStyleProperties = styleProperty; + private readonly OpenXmlLeafElement[]? defaultStyleProperties; protected readonly ParagraphProperties paraProperties = new(); + public BlockElementExpression(IHtmlElement node, OpenXmlLeafElement? styleProperty) : base(node) + { + if (styleProperty is not null) + defaultStyleProperties = [styleProperty]; + } + public BlockElementExpression(IHtmlElement node, params OpenXmlLeafElement[]? styleProperty) : base(node) + { + defaultStyleProperties = styleProperty; + } + /// public override IEnumerable Interpret (ParsingContext context) diff --git a/src/Html2OpenXml/Expressions/BodyExpression.cs b/src/Html2OpenXml/Expressions/BodyExpression.cs index 7ff5ee1..1abd147 100644 --- a/src/Html2OpenXml/Expressions/BodyExpression.cs +++ b/src/Html2OpenXml/Expressions/BodyExpression.cs @@ -23,7 +23,8 @@ namespace HtmlToOpenXml.Expressions; /// Top parent expression, processing the body tag, /// even if it is not directly specified in the provided Html. /// -sealed class BodyExpression(IHtmlElement node) : BlockElementExpression(node) +sealed class BodyExpression(IHtmlElement node, ParagraphStyleId? defaultStyle) + : BlockElementExpression(node, defaultStyle) { private bool shouldRegisterTopBookmark; diff --git a/src/Html2OpenXml/HtmlConverter.cs b/src/Html2OpenXml/HtmlConverter.cs index 8531f7e..7cb7a38 100755 --- a/src/Html2OpenXml/HtmlConverter.cs +++ b/src/Html2OpenXml/HtmlConverter.cs @@ -127,8 +127,7 @@ public async Task ParseHeader(string html, HeaderFooterValues? headerType = null new ParallelOptions() { CancellationToken = cancellationToken }, htmlStyles.GetParagraphStyle(htmlStyles.DefaultStyles.HeaderStyle)); - foreach (var p in paragraphs) - headerPart.Header.AddChild(p); + headerPart.Header.Append(paragraphs); } /// @@ -152,8 +151,7 @@ public async Task ParseFooter(string html, HeaderFooterValues? footerType = null new ParallelOptions() { CancellationToken = cancellationToken }, htmlStyles.GetParagraphStyle(htmlStyles.DefaultStyles.FooterStyle)); - foreach (var p in paragraphs) - footerPart.Footer.AddChild(p); + footerPart.Footer.Append(paragraphs); } /// @@ -166,7 +164,8 @@ public async Task ParseBody(string html, CancellationToken cancellationToken = d { bodyImageLoader ??= new ImagePrefetcher(mainPart, webRequester); var paragraphs = await ParseCoreAsync(html, mainPart, bodyImageLoader, - new ParallelOptions() { CancellationToken = cancellationToken }); + new ParallelOptions() { CancellationToken = cancellationToken }, + htmlStyles.GetParagraphStyle(htmlStyles.DefaultStyles.Paragraph)); if (!paragraphs.Any()) return; @@ -263,11 +262,9 @@ private async Task> ParseCoreAsync(string h Expressions.HtmlDomExpression expression; if (hostingPart is MainDocumentPart) - expression = new Expressions.BodyExpression(htmlDocument.Body!); - else if (defaultParagraphStyleId?.Val?.HasValue == true) - expression = new Expressions.BlockElementExpression(htmlDocument.Body!, defaultParagraphStyleId); + expression = new Expressions.BodyExpression(htmlDocument.Body!, defaultParagraphStyleId); else - expression = new Expressions.BlockElementExpression(htmlDocument.Body!); + expression = new Expressions.BlockElementExpression(htmlDocument.Body!, defaultParagraphStyleId); var parsingContext = new ParsingContext(this, hostingPart, imageLoader); var paragraphs = expression.Interpret(parsingContext); diff --git a/src/Html2OpenXml/PredefinedStyles.cs b/src/Html2OpenXml/PredefinedStyles.cs index a7cf2a5..993307a 100755 --- a/src/Html2OpenXml/PredefinedStyles.cs +++ b/src/Html2OpenXml/PredefinedStyles.cs @@ -24,6 +24,7 @@ internal class PredefinedStyles public const string TableGrid = "TableGrid"; public const string Header = "Header"; public const string Footer = "Footer"; + public const string Paragraph = "Normal"; diff --git a/src/Html2OpenXml/Primitives/DefaultStyles.cs b/src/Html2OpenXml/Primitives/DefaultStyles.cs index 44d2a1c..013adb3 100644 --- a/src/Html2OpenXml/Primitives/DefaultStyles.cs +++ b/src/Html2OpenXml/Primitives/DefaultStyles.cs @@ -101,4 +101,10 @@ public class DefaultStyles /// /// Footer public string FooterStyle { get; set; } = PredefinedStyles.Footer; + + /// + /// Default style for body paragraph. + /// + /// Normal + public string Paragraph { get; set; } = PredefinedStyles.Paragraph; } \ No newline at end of file diff --git a/src/Html2OpenXml/WordDocumentStyle.cs b/src/Html2OpenXml/WordDocumentStyle.cs index 6f7ee26..dd974c4 100755 --- a/src/Html2OpenXml/WordDocumentStyle.cs +++ b/src/Html2OpenXml/WordDocumentStyle.cs @@ -53,7 +53,8 @@ internal WordDocumentStyle(MainDocumentPart mainPart) PredefinedStyles.ListParagraph, PredefinedStyles.Quote, PredefinedStyles.QuoteChar, - PredefinedStyles.TableGrid + PredefinedStyles.TableGrid, + PredefinedStyles.Paragraph ]; this.mainPart = mainPart; } diff --git a/test/HtmlToOpenXml.Tests/BodyTests.cs b/test/HtmlToOpenXml.Tests/BodyTests.cs index 2baeb46..d6da93d 100644 --- a/test/HtmlToOpenXml.Tests/BodyTests.cs +++ b/test/HtmlToOpenXml.Tests/BodyTests.cs @@ -12,9 +12,11 @@ public class BodyTests : HtmlConverterTestBase { [TestCase("landscape", ExpectedResult = true)] [TestCase("portrait", ExpectedResult = false)] - public bool PageOrientation_ReturnsLandscapeDimension(string orientation) + public async Task PageOrientation_ReturnsLandscapeDimension(string orientation) { - var _ = converter.Parse($@""); + await converter.ParseBody($@""); + AssertThatOpenXmlDocumentIsValid(); + var sectionProperties = mainPart.Document.Body!.GetFirstChild(); Assert.That(sectionProperties, Is.Not.Null); var pageSize = sectionProperties.GetFirstChild(); @@ -24,7 +26,7 @@ public bool PageOrientation_ReturnsLandscapeDimension(string orientation) [TestCase("portrait", ExpectedResult = true)] [TestCase("landscape", ExpectedResult = false)] - public bool PageOrientation_OverrideExistingLayout_ReturnsLandscapeDimension(string orientation) + public async Task PageOrientation_OverrideExistingLayout_ReturnsLandscapeDimension(string orientation) { using var generatedDocument = new MemoryStream(); using (var buffer = ResourceHelper.GetStream("Resources.DocWithLandscape.docx")) @@ -35,7 +37,9 @@ public bool PageOrientation_OverrideExistingLayout_ReturnsLandscapeDimension(str MainDocumentPart mainPart = package.MainDocumentPart!; HtmlConverter converter = new(mainPart); - var _ = converter.Parse($@""); + await converter.ParseBody($@""); + AssertThatOpenXmlDocumentIsValid(); + var sectionProperties = mainPart.Document.Body!.GetFirstChild(); Assert.That(sectionProperties, Is.Not.Null); var pageSize = sectionProperties.GetFirstChild(); diff --git a/test/HtmlToOpenXml.Tests/HeaderFooterTests.cs b/test/HtmlToOpenXml.Tests/HeaderFooterTests.cs index 1f399e6..8bb7193 100644 --- a/test/HtmlToOpenXml.Tests/HeaderFooterTests.cs +++ b/test/HtmlToOpenXml.Tests/HeaderFooterTests.cs @@ -109,5 +109,48 @@ public async Task WithExistingHeader_Even_ReturnsAnotherHeaderPart() }); AssertThatOpenXmlDocumentIsValid(); } + + [Test] + public async Task Header_ReturnsStyleParagraphs() + { + await converter.ParseHeader(@" +
+

Placeholder +

+

+
+ "); + + var header = mainPart.HeaderParts.FirstOrDefault()?.Header; + Assert.That(header, Is.Not.Null); + var paragraphs = header.Elements(); + Assert.That(paragraphs.Count(), Is.EqualTo(3)); + Assert.That(paragraphs.First().ParagraphProperties?.ParagraphStyleId?.Val?.Value, + Is.EqualTo(converter.HtmlStyles.DefaultStyles.HeaderStyle)); + Assert.That(paragraphs.Skip(1).Select(p => p.ParagraphProperties?.ParagraphStyleId?.Val?.Value), + Has.All.EqualTo(converter.HtmlStyles.DefaultStyles.ListParagraphStyle)); + } + + [Test] + public async Task Footer_ReturnsStyleParagraphs() + { + await converter.ParseFooter(@" + + "); + + var footer = mainPart.FooterParts.FirstOrDefault()?.Footer; + Assert.That(footer, Is.Not.Null); + var paragraphs = footer.Elements(); + Assert.That(paragraphs.Count(), Is.EqualTo(2)); + Assert.That(paragraphs.Select(p => p.ParagraphProperties?.ParagraphStyleId?.Val?.Value), + Has.All.EqualTo(converter.HtmlStyles.DefaultStyles.FooterStyle)); + } } } \ No newline at end of file