Skip to content

Commit

Permalink
Supports a feature to disable heading numbering #175
Browse files Browse the repository at this point in the history
  • Loading branch information
onizet committed Nov 10, 2024
1 parent d506001 commit 32e9571
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/Html2OpenXml/Expressions/HyperlinkExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
h = new Hyperlink() { History = true, Anchor = "_top" };
}
// is it an anchor?
else if (!context.Converter.ExcludeLinkAnchor && linkNode.Hash.Length > 1 && linkNode.Hash[0] == '#')
else if (context.Converter.SupportsAnchorLinks && linkNode.Hash.Length > 1 && linkNode.Hash[0] == '#')
{
h = new Hyperlink(
) { History = true, Anchor = linkNode.Hash.Substring(1) };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
paragraph.ParagraphProperties ??= new();
paragraph.ParagraphProperties.ParagraphStyleId =
context.DocumentStyle.GetParagraphStyle(context.DocumentStyle.DefaultStyles.HeadingStyle + level);

var runElement = childElements.FirstOrDefault();
if (runElement != null && IsNumbering(runElement))
if (runElement != null && context.Converter.SupportsHeadingNumbering && IsNumbering(runElement))
{
var abstractNumId = GetOrCreateListTemplate(context, HeadingNumberingName);
var instanceId = GetListInstance(abstractNumId);
Expand Down
33 changes: 30 additions & 3 deletions src/Html2OpenXml/HtmlConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ private TPart ResolveHeaderFooterPart<TRefType, TPart>(HeaderFooterValues? type)
public AcronymPosition AcronymPosition { get; set; }

/// <summary>
/// Gets or sets whether anchor links are included or not in the convertion.
/// Gets or sets whether anchor links are included or not in the conversion
/// (defaults <see langword="true" />).
/// </summary>
/// <remarks>An anchor is a term used to define a hyperlink destination inside a document.
/// <see href="http://www.w3schools.com/HTML/html_links.asp"/>.
Expand All @@ -351,7 +352,23 @@ private TPart ResolveHeaderFooterPart<TRefType, TPart>(HeaderFooterValues? type)
/// <see cref="DocumentFormat.OpenXml.Wordprocessing.BookmarkEnd"/> elements
/// and set the value of href to <i><c>#name of your bookmark</c></i>.
/// </remarks>
public bool ExcludeLinkAnchor { get; set; }
public bool SupportsAnchorLinks { get; set; } = true;

/// <summary>
/// Gets or sets whether anchor links are included or not in the conversion.
/// </summary>
/// <remarks>An anchor is a term used to define a hyperlink destination inside a document.
/// <see href="http://www.w3schools.com/HTML/html_links.asp"/>.
/// <br/>
/// It exists some predefined anchors used by Word such as _top to refer to the top of the document.
/// The anchor <i>#_top</i> is always accepted regardless this property value.
/// For others anchors like refering to your own bookmark or a title, add a
/// <see cref="DocumentFormat.OpenXml.Wordprocessing.BookmarkStart"/> and
/// <see cref="DocumentFormat.OpenXml.Wordprocessing.BookmarkEnd"/> elements
/// and set the value of href to <i><c>#name of your bookmark</c></i>.
/// </remarks>
[Obsolete("Use SupportsAnchorLink instead, if ExcludeLinkAnchor = true -> SupportsAnchorLink = false")]
public bool ExcludeLinkAnchor { get => !SupportsAnchorLinks; set => SupportsAnchorLinks = !value; }

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

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

/// <summary>
/// Defines whether any headings (<c>h1-h6</c>) could be considered as multi-level numbering, such as
/// 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.
/// This feature is enabled by default.
/// </summary>
/// <remarks>The converter is detecting headings starting with a number (ie: <c>1.</c> or <c>1 </c>)
/// are considered as numbering.
/// </remarks>
public bool SupportsHeadingNumbering { get; set; } = true;

/// <summary>
/// Gets the mainDocumentPart of the destination OpenXml document.
/// </summary>
Expand Down
26 changes: 26 additions & 0 deletions test/HtmlToOpenXml.Tests/HeadingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ public void OrderedPattern_ReturnsNumberingHeading(string html)
});
}

[TestCase("<h1>1. Heading 1</h1><h2>1.1 Heading Normal Case</h1>")]
[TestCase("<h1>1. Heading 1</h1><h2>1.1 Heading Double Space</h2>", Description = "Double space after number")]
[TestCase("<h1>1. Heading 1</h1><h2>1.2&#09;Heading Tab</h2>", Description = "Tab after number")]
[TestCase("<h1>1. Heading 1</h1><h2>1.3Heading No Space</h2>", Description = "No space after number")]
public void OrderedPattern_DisableNumberingSupports_ReturnsSimpleHeading(string html)
{
converter.SupportsHeadingNumbering = false;
var elements = converter.Parse(html);

var absNum = mainPart.NumberingDefinitionsPart?.Numbering
.Elements<AbstractNum>()
.Where(abs => abs.AbstractNumDefinitionName?.Val == NumberingExpressionBase.HeadingNumberingName)
.SingleOrDefault();
Assert.That(absNum, Is.Null);

var paragraphs = elements.Cast<Paragraph>();
Assert.Multiple(() =>
{
Assert.That(paragraphs.Count(), Is.EqualTo(2));
Assert.That(paragraphs.First().InnerText, Is.EqualTo("1. Heading 1"));
Assert.That(paragraphs.First().ParagraphProperties?.NumberingProperties?.NumberingLevelReference?.Val,
Is.Null,
"First paragraph is not a numbering");
});
}

[Test]
public void MaxLevel_ShouldBeIgnored()
{
Expand Down
2 changes: 1 addition & 1 deletion test/HtmlToOpenXml.Tests/LinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void Anchoring_WithUnknownTarget_ReturnsHyperlinkWithBookmark ()
[Test]
public void SetExcludeAnchoring_ReturnsSimpleRun ()
{
converter.ExcludeLinkAnchor = true;
converter.SupportsAnchorLinks = false;

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

0 comments on commit 32e9571

Please sign in to comment.