Skip to content

Commit 7c61538

Browse files
authored
Merge pull request #170 from onizet/dev
Release 3.2.1
2 parents f1a7850 + ee0dcd7 commit 7c61538

24 files changed

+362
-220
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 3.2.1
4+
5+
- Fix indentation of numbering list #166
6+
- Bordered container must render its content with one bordered frame #168
7+
- Fix serialisation of the "Harvard" style for lower-roman list
8+
- Fix ParseHeader/Footer where input with multiple paragraphs output only the latest
9+
- Ensure to apply default style for paragraphs, to avoid a paragraph between 2 list is mis-guessed
10+
311
## 3.2.0
412

513
- Add new public API to allow parsing into Header and Footer #162. Some API methods as been flagged as obsolete with a clear message of what to use instead.

HtmlToOpenXml.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 17
3+
# Visual Studio Version 17
44
VisualStudioVersion = 17.8.34511.84
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HtmlToOpenXml", "src\Html2OpenXml\HtmlToOpenXml.csproj", "{EF700F30-C9BB-49A6-912C-E3B77857B514}"

examples/Demo/Program.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static class Program
1515
static async Task Main(string[] args)
1616
{
1717
const string filename = "test.docx";
18-
string html = ResourceHelper.GetString("Resources.AdvancedTable.html");
18+
string html = ResourceHelper.GetString("Resources.CompleteRunTest.html");
1919
if (File.Exists(filename)) File.Delete(filename);
2020

2121
using (MemoryStream generatedDocument = new MemoryStream())
@@ -28,8 +28,8 @@ static async Task Main(string[] args)
2828
}
2929

3030
generatedDocument.Position = 0L;
31-
using (WordprocessingDocument package = WordprocessingDocument.Open(generatedDocument, true))
32-
//using (WordprocessingDocument package = WordprocessingDocument.Create(generatedDocument, WordprocessingDocumentType.Document))
31+
//using (WordprocessingDocument package = WordprocessingDocument.Open(generatedDocument, true))
32+
using (WordprocessingDocument package = WordprocessingDocument.Create(generatedDocument, WordprocessingDocumentType.Document))
3333
{
3434
MainDocumentPart mainPart = package.MainDocumentPart;
3535
if (mainPart == null)
@@ -38,12 +38,11 @@ static async Task Main(string[] args)
3838
new Document(new Body()).Save(mainPart);
3939
}
4040

41-
HtmlConverter converter = new HtmlConverter(mainPart);
41+
HtmlConverter converter = new(mainPart, new HtmlToOpenXml.IO.DefaultWebRequest(){
42+
BaseImageUrl = new Uri(Path.Combine(Environment.CurrentDirectory, "images"))
43+
});
4244
converter.RenderPreAsTable = true;
43-
Body body = mainPart.Document.Body;
44-
4545
await converter.ParseBody(html);
46-
mainPart.Document.Save();
4746

4847
AssertThatOpenXmlDocumentIsValid(package);
4948
}

examples/Demo/Resources/CompleteRunTest.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,19 @@ <h5>Heading 5</h5>
161161
line below! </pre>
162162

163163
</div>
164-
164+
165+
<div style='margin-top: 20px; border: 1px dashed rgba(0, 0, 0, 0.4); background: olive; color: white; display: flex; gap: 5px; padding: 6px 8px; font-size: 14px;'>
166+
<div>
167+
<p>Header placeholder:</p>
168+
<ol>
169+
<li>Item 1</li>
170+
<li>Item 2</li>
171+
</ol>
172+
<p style='text-indent: 4.5em'>Footer Placeholder</p>
173+
</div>
174+
</div>
175+
176+
<div style="border: 1px dashed salmon; margin: 1em">Lorem Ipsum</div>
165177
<!--<div style="page-break-before: always">
166178
New page
167179
</div>-->

examples/Demo/Resources/Demo.html

Lines changed: 0 additions & 97 deletions
This file was deleted.

examples/Demo/Resources/Demo2.html

Lines changed: 0 additions & 57 deletions
This file was deleted.

src/Html2OpenXml/Expressions/BlockElementExpression.cs

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,68 @@ namespace HtmlToOpenXml.Expressions;
2323
/// Process the parsing of block contents (like <c>p</c>, <c>span</c>, <c>heading</c>).
2424
/// A block-level element always starts on a new line, and the browsers automatically add some space (a margin) before and after the element.
2525
/// </summary>
26-
class BlockElementExpression(IHtmlElement node, params OpenXmlLeafElement[]? styleProperty) : PhrasingElementExpression(node)
26+
class BlockElementExpression: PhrasingElementExpression
2727
{
28-
private readonly OpenXmlLeafElement[]? defaultStyleProperties = styleProperty;
28+
private readonly OpenXmlLeafElement[]? defaultStyleProperties;
2929
protected readonly ParagraphProperties paraProperties = new();
30+
// some style attributes, such as borders or bgcolor, will convert this node to a framed container
31+
protected bool renderAsFramed;
32+
private HtmlBorder styleBorder;
33+
34+
35+
public BlockElementExpression(IHtmlElement node, OpenXmlLeafElement? styleProperty) : base(node)
36+
{
37+
if (styleProperty is not null)
38+
defaultStyleProperties = [styleProperty];
39+
}
40+
public BlockElementExpression(IHtmlElement node, params OpenXmlLeafElement[]? styleProperty) : base(node)
41+
{
42+
defaultStyleProperties = styleProperty;
43+
}
3044

3145

3246
/// <inheritdoc/>
3347
public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
3448
{
35-
var elements = base.Interpret(context);
49+
var childElements = base.Interpret(context);
3650

3751
var bookmarkTarget = node.GetAttribute(InternalNamespaceUri, "bookmark");
3852
if (bookmarkTarget is not null)
3953
{
4054
var bookmarkId = IncrementBookmarkId(context).ToString(CultureInfo.InvariantCulture);
41-
var p = elements.First();
55+
var p = childElements.First();
4256
// need to be inserted after pPr to avoid schema warning
4357
p.InsertAfter(new BookmarkStart() { Id = bookmarkId, Name = bookmarkTarget }, p.GetFirstChild<ParagraphProperties>());
4458
p.AppendChild(new BookmarkEnd() { Id = bookmarkId });
4559
}
4660

47-
return elements;
61+
if (!renderAsFramed)
62+
return childElements;
63+
64+
var paragraphs = childElements.OfType<Paragraph>();
65+
if (!paragraphs.Any()) return childElements;
66+
67+
// if we have only 1 paragraph, just inline the styles
68+
if (paragraphs.Count() == 1)
69+
{
70+
var p = paragraphs.First();
71+
72+
if (!styleBorder.IsEmpty && p.ParagraphProperties?.ParagraphBorders is null)
73+
{
74+
p.ParagraphProperties ??= new();
75+
p.ParagraphProperties!.ParagraphBorders = new ParagraphBorders {
76+
LeftBorder = Converter.ToBorder<LeftBorder>(styleBorder.Left),
77+
RightBorder = Converter.ToBorder<RightBorder>(styleBorder.Right),
78+
TopBorder = Converter.ToBorder<TopBorder>(styleBorder.Top),
79+
BottomBorder = Converter.ToBorder<BottomBorder>(styleBorder.Bottom)
80+
};
81+
}
82+
83+
return childElements;
84+
}
85+
86+
// if we have 2+ paragraphs, we will embed them inside a stylised table
87+
return [CreateFrame(childElements)];
4888
}
4989

5090
protected override IEnumerable<OpenXmlElement> Interpret (
@@ -136,17 +176,11 @@ protected override void ComposeStyles (ParsingContext context)
136176
}
137177

138178

139-
var styleBorder = styleAttributes.GetBorders();
179+
styleBorder = styleAttributes.GetBorders();
140180
if (!styleBorder.IsEmpty)
141181
{
142-
var borders = new ParagraphBorders {
143-
LeftBorder = Converter.ToBorder<LeftBorder>(styleBorder.Left),
144-
RightBorder = Converter.ToBorder<RightBorder>(styleBorder.Right),
145-
TopBorder = Converter.ToBorder<TopBorder>(styleBorder.Top),
146-
BottomBorder = Converter.ToBorder<BottomBorder>(styleBorder.Bottom)
147-
};
148-
149-
paraProperties.ParagraphBorders = borders;
182+
renderAsFramed = true;
183+
runProperties.Border = null;
150184
}
151185

152186
foreach (string className in node.ClassList)
@@ -159,8 +193,8 @@ protected override void ComposeStyles (ParsingContext context)
159193
}
160194
}
161195

162-
Margin margin = styleAttributes.GetMargin("margin");
163-
Indentation? indentation = null;
196+
var margin = styleAttributes.GetMargin("margin");
197+
Indentation? indentation = null;
164198
if (!margin.IsEmpty)
165199
{
166200
if (margin.Top.IsFixed || margin.Bottom.IsFixed)
@@ -236,6 +270,9 @@ protected override void ComposeStyles (ParsingContext context)
236270
};
237271
}
238272
}
273+
274+
if (runProperties.Shading != null)
275+
renderAsFramed = true;
239276
}
240277

241278
/// <summary>
@@ -322,6 +359,43 @@ private static Paragraph CreateParagraph(ParsingContext context, IList<OpenXmlEl
322359
return p;
323360
}
324361

362+
363+
/// <summary>
364+
/// Group all the paragraph inside a framed table.
365+
/// </summary>
366+
private Table CreateFrame(IEnumerable<OpenXmlElement> childElements)
367+
{
368+
TableCell cell;
369+
TableProperties tableProperties;
370+
Table framedTable = new(
371+
tableProperties = new TableProperties {
372+
TableWidth = new() { Type = TableWidthUnitValues.Pct, Width = "5000" } // 100%
373+
},
374+
new TableGrid(
375+
new GridColumn() { Width = "9442" }),
376+
new TableRow(
377+
cell = new TableCell(childElements)
378+
)
379+
);
380+
381+
if (!styleBorder.IsEmpty)
382+
{
383+
tableProperties.TableBorders = new TableBorders {
384+
LeftBorder = Converter.ToBorder<LeftBorder>(styleBorder.Left),
385+
RightBorder = Converter.ToBorder<RightBorder>(styleBorder.Right),
386+
TopBorder = Converter.ToBorder<TopBorder>(styleBorder.Top),
387+
BottomBorder = Converter.ToBorder<BottomBorder>(styleBorder.Bottom)
388+
};
389+
}
390+
391+
if (runProperties.Shading != null)
392+
{
393+
cell.TableCellProperties = new() { Shading = (Shading?) runProperties.Shading.Clone() };
394+
}
395+
396+
return framedTable;
397+
}
398+
325399
/// <summary>
326400
/// Resolve the next available <see cref="BookmarkStart.Id"/> (they must be unique).
327401
/// </summary>

0 commit comments

Comments
 (0)