Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Continous Integration Pipeline #153

Merged
merged 20 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: 'dotnet.yml'

on:
push:
branches: [ "dev" ]
paths-ignore:
- 'docs/**'
- '**/*.md'
pull_request:
branches: [ "dev" ]

jobs:
net8:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET 8.
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Run tests
run: dotnet test --framework net8.0 --configuration Release --no-build --verbosity normal

net462:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET Framework
uses: microsoft/setup-msbuild@v2
- name: Build
run: msbuild test/HtmlToOpenXml.Tests/HtmlToOpenXml.Tests.csproj /p:Configuration=Release /p:TargetFramework=net462 /restore
- name: Setup VSTest
uses: darenm/[email protected]
- name: Run tests (NET Framework)
run: vstest.console.exe test\HtmlToOpenXml.Tests\bin\Release\net462\HtmlToOpenXml.Tests.dll /parallel
9 changes: 0 additions & 9 deletions .travis.yml

This file was deleted.

8 changes: 8 additions & 0 deletions examples/Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ static async Task Main(string[] args)
const string filename = "test.docx";
string html = ResourceHelper.GetString("Resources.CompleteRunTest.html");
if (File.Exists(filename)) File.Delete(filename);
const string preformattedText = @"
^__^
(oo)\_______
(__)\ )\/\
||----w |
|| ||";

html = @$"<pre role='img' aria-label='ASCII COW'>
{preformattedText}</pre>";
using (MemoryStream generatedDocument = new MemoryStream())
{
// Uncomment and comment the second using() to open an existing template document
Expand Down
2 changes: 1 addition & 1 deletion src/Html2OpenXml/Expressions/ParsingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ sealed class ParsingContext(HtmlConverter converter, MainDocumentPart mainPart)
private Dictionary<string, object?> propertyBag = [];

/// <summary>Whether the text content should preserve the line breaks.</summary>
public bool PreverseLinebreaks { get; set; }
public bool PreserveLinebreaks { get; set; }

/// <summary>Whether the text content should collapse the whitespaces.</summary>
public bool CollapseWhitespaces { get; set; } = true;
Expand Down
2 changes: 1 addition & 1 deletion src/Html2OpenXml/Expressions/PreElementExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public override IEnumerable<OpenXmlElement> Interpret(ParsingContext context)
{
ComposeStyles(context);
var childContext = context.CreateChild(this);
childContext.PreverseLinebreaks = true;
childContext.PreserveLinebreaks = true;
childContext.CollapseWhitespaces = false;
var childElements = Interpret(childContext, node.ChildNodes);

Expand Down
35 changes: 31 additions & 4 deletions src/Html2OpenXml/Expressions/TextExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
string text = node.TextContent.Normalize();
if (text.Trim().Length == 0) return [];

if (!context.PreverseLinebreaks)
if (!context.PreserveLinebreaks)
text = text.CollapseLineBreaks();
if (context.CollapseWhitespaces && text[0].IsWhiteSpaceCharacter() &&
node.PreviousSibling is IHtmlImageElement)
Expand All @@ -41,9 +41,36 @@ public override IEnumerable<OpenXmlElement> Interpret (ParsingContext context)
else if (context.CollapseWhitespaces)
text = text.CollapseAndStrip();

Run run = new(
new Text(text)
);
if (!context.PreserveLinebreaks)
return [new Run(new Text(text))];

var run = new Run();
char[] chars = text.ToCharArray();
int shift = 0, c = 0;
bool wasCR = false; // avoid adding 2 breaks for \r\n
for ( ; c < chars.Length ; c++)
{
if (!chars[c].IsLineBreak())
{
wasCR = false;
continue;
}

if (wasCR) continue;
wasCR = chars[c] == Symbols.CarriageReturn;

if (c > 1)
{
run.Append(new Text(new string(chars, shift, c - shift))
{ Space = SpaceProcessingModeValues.Preserve });
run.Append(new Break());
}
shift = c + 1;
}

if (c > shift)
run.Append(new Text(new string(chars, shift, c - shift))
{ Space = SpaceProcessingModeValues.Preserve });

return [run];
}
Expand Down
3 changes: 2 additions & 1 deletion test/HtmlToOpenXml.Tests/HtmlToOpenXml.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net462;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<SignAssembly>true</SignAssembly>
Expand Down
13 changes: 12 additions & 1 deletion test/HtmlToOpenXml.Tests/TableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,22 @@ public void ParsePreAsTable()
var cell = cells.First();
Assert.Multiple(() =>
{
Assert.That(cell.InnerText, Is.EqualTo(preformattedText));
Assert.That(cell.TableCellProperties?.TableCellBorders.ChildElements.Count(), Is.EqualTo(4));
Assert.That(cell.TableCellProperties?.TableCellBorders.ChildElements, Has.All.InstanceOf<BorderType>());
Assert.That(cell.TableCellProperties?.TableCellBorders.Elements<BorderType>().All(b => b.Val.Value == BorderValues.Single), Is.True);
});

var run = cell.GetFirstChild<Paragraph>()?.GetFirstChild<Run>();
Assert.Multiple(() =>
{
var odds = run.ChildElements.Where((item, index) => index % 2 != 0);
Assert.That(odds, Has.All.TypeOf<Break>());
Assert.That(run.ChildElements.ElementAt(0).InnerText, Is.EqualTo(" ^__^"));
Assert.That(run.ChildElements.ElementAt(2).InnerText, Is.EqualTo(" (oo)\\_______"));
Assert.That(run.ChildElements.ElementAt(4).InnerText, Is.EqualTo(" (__)\\ )\\/\\"));
Assert.That(run.ChildElements.ElementAt(6).InnerText, Is.EqualTo(" ||----w |"));
Assert.That(run.ChildElements.ElementAt(8).InnerText, Is.EqualTo(" || ||"));
});
}

[Test]
Expand Down
123 changes: 123 additions & 0 deletions test/HtmlToOpenXml.Tests/Utilities/CollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Linq;

#if NET462

/// <summary>
/// Helper class that provide some extension methods to <see cref="IEnumerable{T}"/> API.
/// </summary>
static class CollectionExtensions
{
/// <summary>
/// Attempts to add the specified key and value to the dictionary.
/// </summary>
/// <param name="dictionary">The dictionary in which to insert the item.</param>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add. It can be <see langword="null"/>.</param>
/// <returns><see langword="true"/> if the key/value pair was added to the dictionary successfully; otherwise, <see langword="false"/>.</returns>
/// <summary>Returns the maximum value in a generic sequence according to a specified key selector function.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
/// <typeparam name="TKey">The type of key to compare elements by.</typeparam>
/// <param name="source">A sequence of values to determine the maximum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="comparer">The <see cref="IComparer{TKey}" /> to compare keys.</param>
/// <returns>The value with the maximum key in the sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">No key extracted from <paramref name="source" /> implements the <see cref="IComparable" /> or <see cref="IComparable{TKey}" /> interface.</exception>
/// <remarks>
/// <para>If <typeparamref name="TKey" /> is a reference type and the source sequence is empty or contains only values that are <see langword="null" />, this method returns <see langword="null" />.</para>
/// </remarks>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
if (source == null)
throw new ArgumentNullException(nameof(source));

if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));

var comparer = Comparer<TKey>.Default;

using IEnumerator<TSource> e = source.GetEnumerator();

if (!e.MoveNext())
{
if (default(TSource) is null)
{
return default;
}
else
{
throw new InvalidOperationException("Sequence contains no elements");
}
}

TSource value = e.Current;
TKey key = keySelector(value);

if (default(TKey) is null)
{
if (key == null)
{
TSource firstValue = value;

do
{
if (!e.MoveNext())
{
// All keys are null, surface the first element.
return firstValue;
}

value = e.Current;
key = keySelector(value);
}
while (key == null);
}

while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (nextKey != null && comparer.Compare(nextKey, key) > 0)
{
key = nextKey;
value = nextValue;
}
}
}
else
{
if (comparer == Comparer<TKey>.Default)
{
while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (Comparer<TKey>.Default.Compare(nextKey, key) > 0)
{
key = nextKey;
value = nextValue;
}
}
}
else
{
while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (comparer.Compare(nextKey, key) > 0)
{
key = nextKey;
value = nextValue;
}
}
}
}

return value;
}
}
#endif
Loading