diff --git a/.github/workflows/publish_nuget.yml b/.github/workflows/publish_nuget.yml new file mode 100644 index 00000000..234443ae --- /dev/null +++ b/.github/workflows/publish_nuget.yml @@ -0,0 +1,36 @@ +# This workflow will build, pack and deploy the solution on Nuget + +name: 'publish_nuget.yml' + +on: + push: + tags: + - '[0-9]+.[0-9]+' + branches: + - master + +jobs: + publish: + if: startsWith(github.ref, 'refs/tags/') && github.ref_type == 'tag' && github.ref_name != '' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Ensure tag is on master + run: | + TAG_COMMIT=$(git rev-list -n 1 ${{ github.ref_name }}) + if ! git merge-base --is-ancestor $TAG_COMMIT origin/master; then + echo "Tag is not on master branch. Skipping publish." + exit 1 + fi + - name: Setup .NET 8. + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + - name: Build + run: dotnet build --configuration Release + - name: Pack + run: dotnet pack src/Html2OpenXml/HtmlToOpenXml.csproj --configuration Release --output ./nupkg + - name: Push nuget to NuGet.org + run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json + - name: Create Release and Upload Artifact to Release + run: gh release create ${{github.ref_name}} -t "Release ${{github.ref_name}}" *.nupkg --generate-notes --draft diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b0af9c8..0b299a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 3.2.6 + +- Fix handling Uri with an anchor #209 +- New option DefaultStyles.NumberedHeadingStyle to support an alternate heading style #210 + ## 3.2.5 - Fix a crash with the new whitespace handling introduced in 3.2.3 #191 diff --git a/src/Html2OpenXml/Expressions/HyperlinkExpression.cs b/src/Html2OpenXml/Expressions/HyperlinkExpression.cs index 62221ecd..79fd8bee 100644 --- a/src/Html2OpenXml/Expressions/HyperlinkExpression.cs +++ b/src/Html2OpenXml/Expressions/HyperlinkExpression.cs @@ -112,12 +112,6 @@ public override IEnumerable Interpret (ParsingContext context) { h = new Hyperlink() { History = true, Anchor = "_top" }; } - // is it an anchor? - else if (context.Converter.SupportsAnchorLinks && linkNode.Hash.Length > 1 && linkNode.Hash[0] == '#') - { - h = new Hyperlink( - ) { History = true, Anchor = linkNode.Hash.Substring(1) }; - } // ensure the links does not start with javascript: else if (AngleSharpExtensions.TryParseUrl(att, UriKind.Absolute, out var uri)) { @@ -126,6 +120,13 @@ public override IEnumerable Interpret (ParsingContext context) h = new Hyperlink( ) { History = true, Id = extLink.Id }; } + // is it an anchor? + else if (context.Converter.SupportsAnchorLinks && linkNode.Hash.Length > 1 && linkNode.Hash[0] == '#') + { + h = new Hyperlink( + ) + { History = true, Anchor = linkNode.Hash.Substring(1) }; + } if (h == null) { diff --git a/src/Html2OpenXml/Expressions/Numbering/HeadingElementExpression.cs b/src/Html2OpenXml/Expressions/Numbering/HeadingElementExpression.cs index 1039fae8..24b34ad7 100644 --- a/src/Html2OpenXml/Expressions/Numbering/HeadingElementExpression.cs +++ b/src/Html2OpenXml/Expressions/Numbering/HeadingElementExpression.cs @@ -40,26 +40,43 @@ public override IEnumerable Interpret (ParsingContext context) paragraph ??= new(childElements); paragraph.ParagraphProperties ??= new(); - paragraph.ParagraphProperties.ParagraphStyleId = - context.DocumentStyle.GetParagraphStyle(context.DocumentStyle.DefaultStyles.HeadingStyle + level); - + var runElement = childElements.FirstOrDefault(); if (runElement != null && context.Converter.SupportsHeadingNumbering && IsNumbering(runElement)) { - var abstractNumId = GetOrCreateListTemplate(context, HeadingNumberingName); - var instanceId = GetListInstance(abstractNumId); - if (!instanceId.HasValue) + if (string.Equals(context.DocumentStyle.DefaultStyles.HeadingStyle, context.DocumentStyle.DefaultStyles.NumberedHeadingStyle)) { - instanceId = IncrementInstanceId(context, abstractNumId); + // Only apply the numbering if a custom numbered heading style has not been defined. + // If the user defined a custom numbered heading style (with numbering), Word has + // the numbering automatically done. + // Defining a numbering here messes that up, so we only add the numbering if + // a specific numbering heading style has not been provided + var abstractNumId = GetOrCreateListTemplate(context, HeadingNumberingName); + var instanceId = GetListInstance(abstractNumId); + + if (!instanceId.HasValue) + { + instanceId = IncrementInstanceId(context, abstractNumId); + } + + var numbering = context.MainPart.NumberingDefinitionsPart!.Numbering!; + numbering.Append( + new NumberingInstance( + new AbstractNumId() { Val = abstractNumId } + ) + { NumberID = instanceId }); + SetNumbering(paragraph, level - '0', instanceId.Value); } - var numbering = context.MainPart.NumberingDefinitionsPart!.Numbering!; - numbering.Append( - new NumberingInstance( - new AbstractNumId() { Val = abstractNumId } - ) - { NumberID = instanceId }); - SetNumbering(paragraph, level - '0', instanceId.Value); + // Apply numbered heading style + paragraph.ParagraphProperties.ParagraphStyleId = + context.DocumentStyle.GetParagraphStyle(context.DocumentStyle.DefaultStyles.NumberedHeadingStyle + level); + } + else + { + // Apply normal heading style + paragraph.ParagraphProperties.ParagraphStyleId = + context.DocumentStyle.GetParagraphStyle(context.DocumentStyle.DefaultStyles.HeadingStyle + level); } return [paragraph]; diff --git a/src/Html2OpenXml/HtmlToOpenXml.csproj b/src/Html2OpenXml/HtmlToOpenXml.csproj index 917d8d34..19858b4d 100644 --- a/src/Html2OpenXml/HtmlToOpenXml.csproj +++ b/src/Html2OpenXml/HtmlToOpenXml.csproj @@ -9,13 +9,13 @@ HtmlToOpenXml HtmlToOpenXml HtmlToOpenXml.dll - 3.2.5 + 3.2.6 icon.png Copyright 2009-$([System.DateTime]::Now.Year) Olivier Nizet See changelog https://github.com/onizet/html2openxml/blob/master/CHANGELOG.md README.md office openxml netcore html - 3.2.5 + 3.2.6 MIT https://github.com/onizet/html2openxml https://github.com/onizet/html2openxml diff --git a/src/Html2OpenXml/Primitives/DefaultStyles.cs b/src/Html2OpenXml/Primitives/DefaultStyles.cs index 013adb3a..3512e68f 100644 --- a/src/Html2OpenXml/Primitives/DefaultStyles.cs +++ b/src/Html2OpenXml/Primitives/DefaultStyles.cs @@ -54,6 +54,13 @@ public class DefaultStyles /// Heading public string HeadingStyle { get; set; } = PredefinedStyles.Heading; + /// + /// Default style for numbered headings + /// Appends the level at the end of the style name + /// + /// Heading + public string NumberedHeadingStyle { get; set; } = PredefinedStyles.Heading; + /// /// Default style for hyperlinks /// diff --git a/test/HtmlToOpenXml.Tests/LinkTests.cs b/test/HtmlToOpenXml.Tests/LinkTests.cs index 3aa1e738..1d122888 100644 --- a/test/HtmlToOpenXml.Tests/LinkTests.cs +++ b/test/HtmlToOpenXml.Tests/LinkTests.cs @@ -16,10 +16,11 @@ public class LinkTests : HtmlConverterTestBase [TestCase("://www.site.com")] [TestCase("www.site.com")] [TestCase("http://www.site.com")] - public void ExternalLink_ShouldSucceed (string link) + [TestCase("http://www.site.com/#anchor1", "http://www.site.com/#anchor1")] + public void ExternalLink_ShouldSucceed(string link, string expectedUri = "http://www.site.com/") { var elements = converter.Parse($@"Test Caption"); - AssertHyperlink(mainPart, elements); + AssertHyperlink(mainPart, elements, expectedUri); } [TestCase(@"Js")] @@ -193,7 +194,7 @@ public async Task ParseIntoDocumentPart_ReturnsHyperlinkParentedToPart (Type ope throw new NotSupportedException($"Test case not supported for {openXmlPartType.FullName}"); } - AssertHyperlink(container, host.ChildElements); + AssertHyperlink(container, host.ChildElements, "http://www.site.com/"); AssertThatOpenXmlDocumentIsValid(); } @@ -249,7 +250,8 @@ await converter.ParseBody(@"Move to top Assert.That(rel.Uri.ToString(), Is.EqualTo("#_top")); } - private static void AssertHyperlink(OpenXmlPartContainer container, IEnumerable elements) + private static void AssertHyperlink(OpenXmlPartContainer container, IEnumerable elements, + string expectedUri) { Assert.That(elements.Count(), Is.EqualTo(1)); Assert.Multiple(() => { @@ -272,7 +274,7 @@ private static void AssertHyperlink(OpenXmlPartContainer container, IEnumerable< var extLink = container.HyperlinkRelationships.FirstOrDefault(r => r.Id == hyperlink.Id); Assert.That(extLink, Is.Not.Null); Assert.That(extLink.IsExternal, Is.EqualTo(true)); - Assert.That(extLink.Uri.AbsoluteUri, Is.EqualTo("http://www.site.com/")); + Assert.That(extLink.Uri.AbsoluteUri, Is.EqualTo(expectedUri)); } } } \ No newline at end of file diff --git a/test/HtmlToOpenXml.Tests/Utilities/MockHttpMessageHandler.cs b/test/HtmlToOpenXml.Tests/Utilities/MockHttpMessageHandler.cs index af258be8..b93c4e09 100644 --- a/test/HtmlToOpenXml.Tests/Utilities/MockHttpMessageHandler.cs +++ b/test/HtmlToOpenXml.Tests/Utilities/MockHttpMessageHandler.cs @@ -16,7 +16,7 @@ public MockHttpMessageHandler(Func> getResponseFu protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - return await _getResponseFunc(request.RequestUri); + return await _getResponseFunc(request.RequestUri!); } } } \ No newline at end of file