9
9
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
10
10
* PARTICULAR PURPOSE.
11
11
*/
12
+ using System ;
12
13
using System . Collections . Generic ;
13
14
using System . Linq ;
14
15
using System . Text . RegularExpressions ;
@@ -23,7 +24,8 @@ namespace HtmlToOpenXml.Expressions;
23
24
/// </summary>
24
25
sealed class HeadingElementExpression ( IHtmlElement node ) : NumberingExpressionBase ( node )
25
26
{
26
- private static readonly Regex numberingRegex = new ( @"^\s*(\d+\.?)*\s*" ) ;
27
+ private static readonly Regex numberingRegex = new ( @"^\s*(?<number>[0-9\.]+\s*)[^0-9]" ,
28
+ RegexOptions . Compiled , TimeSpan . FromMilliseconds ( 100 ) ) ;
27
29
28
30
/// <inheritdoc/>
29
31
public override IEnumerable < OpenXmlElement > Interpret ( ParsingContext context )
@@ -36,7 +38,7 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
36
38
37
39
var paragraph = childElements . FirstOrDefault ( ) as Paragraph ;
38
40
39
- paragraph ??= new Paragraph ( childElements ) ;
41
+ paragraph ??= new ( childElements ) ;
40
42
paragraph . ParagraphProperties ??= new ( ) ;
41
43
paragraph . ParagraphProperties . ParagraphStyleId =
42
44
context . DocumentStyle . GetParagraphStyle ( context . DocumentStyle . DefaultStyles . HeadingStyle + level ) ;
@@ -65,16 +67,30 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
65
67
66
68
private static bool IsNumbering ( OpenXmlElement runElement )
67
69
{
70
+ if ( runElement . InnerText is null )
71
+ return false ;
72
+
68
73
// Check if the line starts with a number format (1., 1.1., 1.1.1.)
69
74
// If it does, make sure we make the heading a numbered item
70
- Match regexMatch = numberingRegex . Match ( runElement . InnerText ?? string . Empty ) ;
75
+ var headingText = runElement . InnerText ;
76
+ Match regexMatch ;
77
+ try
78
+ {
79
+ regexMatch = numberingRegex . Match ( headingText ) ;
80
+ }
81
+ catch ( RegexMatchTimeoutException )
82
+ {
83
+ return false ;
84
+ }
85
+
71
86
72
87
// Make sure we only grab the heading if it starts with a number
73
- if ( regexMatch . Groups . Count > 1 && regexMatch . Groups [ 1 ] . Captures . Count > 0 )
88
+ if ( regexMatch . Success && headingText . Length > regexMatch . Groups [ "number" ] . Length )
74
89
{
75
- // Strip numbers from text
90
+ // Strip numbers from text
91
+ headingText = headingText . Substring ( regexMatch . Groups [ "number" ] . Length ) ;
76
92
runElement . InnerXml = runElement . InnerXml
77
- . Replace ( runElement . InnerText ! , runElement . InnerText ! . Substring ( regexMatch . Length ) ) ;
93
+ . Replace ( runElement . InnerText ! , headingText ) ;
78
94
79
95
return true ;
80
96
}
0 commit comments