Skip to content

Commit 32e9571

Browse files
committed
Supports a feature to disable heading numbering #175
1 parent d506001 commit 32e9571

File tree

5 files changed

+60
-7
lines changed

5 files changed

+60
-7
lines changed

src/Html2OpenXml/Expressions/HyperlinkExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
113113
h = new Hyperlink() { History = true, Anchor = "_top" };
114114
}
115115
// is it an anchor?
116-
else if (!context.Converter.ExcludeLinkAnchor && linkNode.Hash.Length > 1 && linkNode.Hash[0] == '#')
116+
else if (context.Converter.SupportsAnchorLinks && linkNode.Hash.Length > 1 && linkNode.Hash[0] == '#')
117117
{
118118
h = new Hyperlink(
119119
) { History = true, Anchor = linkNode.Hash.Substring(1) };

src/Html2OpenXml/Expressions/Numbering/HeadingElementExpression.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
4040
paragraph.ParagraphProperties ??= new();
4141
paragraph.ParagraphProperties.ParagraphStyleId =
4242
context.DocumentStyle.GetParagraphStyle(context.DocumentStyle.DefaultStyles.HeadingStyle + level);
43-
43+
4444
var runElement = childElements.FirstOrDefault();
45-
if (runElement != null && IsNumbering(runElement))
45+
if (runElement != null && context.Converter.SupportsHeadingNumbering && IsNumbering(runElement))
4646
{
4747
var abstractNumId = GetOrCreateListTemplate(context, HeadingNumberingName);
4848
var instanceId = GetListInstance(abstractNumId);

src/Html2OpenXml/HtmlConverter.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ private TPart ResolveHeaderFooterPart<TRefType, TPart>(HeaderFooterValues? type)
339339
public AcronymPosition AcronymPosition { get; set; }
340340

341341
/// <summary>
342-
/// Gets or sets whether anchor links are included or not in the convertion.
342+
/// Gets or sets whether anchor links are included or not in the conversion
343+
/// (defaults <see langword="true" />).
343344
/// </summary>
344345
/// <remarks>An anchor is a term used to define a hyperlink destination inside a document.
345346
/// <see href="http://www.w3schools.com/HTML/html_links.asp"/>.
@@ -351,7 +352,23 @@ private TPart ResolveHeaderFooterPart<TRefType, TPart>(HeaderFooterValues? type)
351352
/// <see cref="DocumentFormat.OpenXml.Wordprocessing.BookmarkEnd"/> elements
352353
/// and set the value of href to <i><c>#name of your bookmark</c></i>.
353354
/// </remarks>
354-
public bool ExcludeLinkAnchor { get; set; }
355+
public bool SupportsAnchorLinks { get; set; } = true;
356+
357+
/// <summary>
358+
/// Gets or sets whether anchor links are included or not in the conversion.
359+
/// </summary>
360+
/// <remarks>An anchor is a term used to define a hyperlink destination inside a document.
361+
/// <see href="http://www.w3schools.com/HTML/html_links.asp"/>.
362+
/// <br/>
363+
/// It exists some predefined anchors used by Word such as _top to refer to the top of the document.
364+
/// The anchor <i>#_top</i> is always accepted regardless this property value.
365+
/// For others anchors like refering to your own bookmark or a title, add a
366+
/// <see cref="DocumentFormat.OpenXml.Wordprocessing.BookmarkStart"/> and
367+
/// <see cref="DocumentFormat.OpenXml.Wordprocessing.BookmarkEnd"/> elements
368+
/// and set the value of href to <i><c>#name of your bookmark</c></i>.
369+
/// </remarks>
370+
[Obsolete("Use SupportsAnchorLink instead, if ExcludeLinkAnchor = true -> SupportsAnchorLink = false")]
371+
public bool ExcludeLinkAnchor { get => !SupportsAnchorLinks; set => SupportsAnchorLinks = !value; }
355372

356373
/// <summary>
357374
/// Gets the Html styles manager mapping to OpenXml style properties.
@@ -367,7 +384,7 @@ public WordDocumentStyle HtmlStyles
367384
public CaptionPositionValues TableCaptionPosition { get; set; }
368385

369386
/// <summary>
370-
/// Gets or sets whether the <c>pre</c> tag should be rendered as a table (default <see langword="false"/>).
387+
/// Gets or sets whether the <c>pre</c> tag should be rendered as a table (defaults <see langword="false"/>).
371388
/// </summary>
372389
/// <remarks>The table will contains only one cell.</remarks>
373390
public bool RenderPreAsTable { get; set; }
@@ -378,6 +395,16 @@ public WordDocumentStyle HtmlStyles
378395
/// </summary>
379396
public bool ContinueNumbering { get; set; } = true;
380397

398+
/// <summary>
399+
/// Defines whether any headings (<c>h1-h6</c>) could be considered as multi-level numbering, such as
400+
/// top-level headings (Heading 1) are numbered 1, 2, 3, for example, and second-level headings (Heading 2) are numbered 1.1, 1.2, 1.3.
401+
/// This feature is enabled by default.
402+
/// </summary>
403+
/// <remarks>The converter is detecting headings starting with a number (ie: <c>1.</c> or <c>1 </c>)
404+
/// are considered as numbering.
405+
/// </remarks>
406+
public bool SupportsHeadingNumbering { get; set; } = true;
407+
381408
/// <summary>
382409
/// Gets the mainDocumentPart of the destination OpenXml document.
383410
/// </summary>

test/HtmlToOpenXml.Tests/HeadingTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,32 @@ public void OrderedPattern_ReturnsNumberingHeading(string html)
5151
});
5252
}
5353

54+
[TestCase("<h1>1. Heading 1</h1><h2>1.1 Heading Normal Case</h1>")]
55+
[TestCase("<h1>1. Heading 1</h1><h2>1.1 Heading Double Space</h2>", Description = "Double space after number")]
56+
[TestCase("<h1>1. Heading 1</h1><h2>1.2&#09;Heading Tab</h2>", Description = "Tab after number")]
57+
[TestCase("<h1>1. Heading 1</h1><h2>1.3Heading No Space</h2>", Description = "No space after number")]
58+
public void OrderedPattern_DisableNumberingSupports_ReturnsSimpleHeading(string html)
59+
{
60+
converter.SupportsHeadingNumbering = false;
61+
var elements = converter.Parse(html);
62+
63+
var absNum = mainPart.NumberingDefinitionsPart?.Numbering
64+
.Elements<AbstractNum>()
65+
.Where(abs => abs.AbstractNumDefinitionName?.Val == NumberingExpressionBase.HeadingNumberingName)
66+
.SingleOrDefault();
67+
Assert.That(absNum, Is.Null);
68+
69+
var paragraphs = elements.Cast<Paragraph>();
70+
Assert.Multiple(() =>
71+
{
72+
Assert.That(paragraphs.Count(), Is.EqualTo(2));
73+
Assert.That(paragraphs.First().InnerText, Is.EqualTo("1. Heading 1"));
74+
Assert.That(paragraphs.First().ParagraphProperties?.NumberingProperties?.NumberingLevelReference?.Val,
75+
Is.Null,
76+
"First paragraph is not a numbering");
77+
});
78+
}
79+
5480
[Test]
5581
public void MaxLevel_ShouldBeIgnored()
5682
{

test/HtmlToOpenXml.Tests/LinkTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public void Anchoring_WithUnknownTarget_ReturnsHyperlinkWithBookmark ()
8282
[Test]
8383
public void SetExcludeAnchoring_ReturnsSimpleRun ()
8484
{
85-
converter.ExcludeLinkAnchor = true;
85+
converter.SupportsAnchorLinks = false;
8686

8787
// _top is always present and bypass the previous rule
8888
var elements = converter.Parse(@"<a href=""#_top"">Anchor2</a>");

0 commit comments

Comments
 (0)