Skip to content
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
8 changes: 4 additions & 4 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: .NET build and test
env:
CURRENT_VERSION: 8.0.${{ github.run_number }}
CURRENT_VERSION: 9.0.${{ github.run_number }}
LAST_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}

on:
Expand All @@ -14,7 +14,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand All @@ -37,7 +37,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand Down
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Build and Test Instructions

## Prerequisites
- .NET 8 SDK (`dotnet-sdk-8.0`) must be installed.
```bash
apt-get update && apt-get install -y dotnet-sdk-8.0
```

## Build
From the repository root, run:
```bash
dotnet build
```
This restores NuGet packages and compiles the library and example projects.

## Test
Execute the unit tests with:
```bash
dotnet test
```
The command builds required projects and runs the test suite.
56 changes: 56 additions & 0 deletions OpenXmlPowerTools.Tests/ColorParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Codeuctivity.OpenXmlPowerTools;
using SkiaSharp;
using Xunit;

namespace Codeuctivity.Tests
{
public class ColorParserTests
{
[Theory]
[InlineData("red", 255, 0, 0)]
[InlineData("RED", 255, 0, 0)]
[InlineData("#00FF00", 0, 255, 0)]
[InlineData("#00ff00", 0, 255, 0)]
[InlineData("blue", 0, 0, 255)]
[InlineData("#0000FF", 0, 0, 255)]
[InlineData("#abc", 170, 187, 204)]
[InlineData("yellow", 255, 255, 0)]
[InlineData("black", 0, 0, 0)]
[InlineData("white", 255, 255, 255)]
public void ShouldParseColors(string input, byte r, byte g, byte b)
{
var result = ColorParser.FromName(input);
Assert.Equal(r, result.Red);
Assert.Equal(g, result.Green);
Assert.Equal(b, result.Blue);
}

[Theory]
[InlineData("red", true)]
[InlineData("#123456", true)]
[InlineData("notacolor", false)]
[InlineData("", false)]
public void ShouldValidateColorNames(string input, bool valid)
{
Assert.Equal(valid, ColorParser.IsValidName(input));
}

[Fact]
public void FromNameShouldThrowOnInvalid()
{
Assert.Throws<System.ArgumentException>(() => ColorParser.FromName("bogus"));
}

[Theory]
[InlineData("notacolor")]
[InlineData("#GGGGGG")]
[InlineData("")]
[InlineData(null)]
[InlineData(" ")]
public void ShouldRejectInvalidColors(string? input)
{
var success = ColorParser.TryFromName(input, out SKColor _);
Assert.False(success);
}
}
}
45 changes: 45 additions & 0 deletions OpenXmlPowerTools.Tests/CssPropertyValueTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Codeuctivity.OpenXmlPowerTools;
using SkiaSharp;
using Xunit;

namespace Codeuctivity.Tests
{
public class CssPropertyValueTests
{
[Fact]
public void ShouldRecognizeNamedColor()
{
var value = new CssPropertyValue { Type = CssValueType.String, Value = "red" };
Assert.True(value.IsColor);
var color = value.ToColor();
Assert.Equal(SKColors.Red, color);
}

[Fact]
public void ShouldRecognizeHexColor()
{
var value = new CssPropertyValue { Type = CssValueType.Hex, Value = "#0000FF" };
Assert.True(value.IsColor);
var color = value.ToColor();
Assert.Equal(SKColors.Blue, color);
}

[Fact]
public void ShouldRejectNonColor()
{
var value = new CssPropertyValue { Type = CssValueType.String, Value = "1234" };
Assert.False(value.IsColor);
}

[Fact]
public void ShouldParseHexWithoutHash()
{
var value = new CssPropertyValue { Type = CssValueType.Hex, Value = "00FF00" };
Assert.True(value.IsColor);
var color = value.ToColor();
Assert.Equal(0u, color.Red);
Assert.Equal(255u, color.Green);
Assert.Equal(0u, color.Blue);
}
}
}
93 changes: 93 additions & 0 deletions OpenXmlPowerTools.Tests/ImageHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System.IO;
using System.Xml.Linq;
using Codeuctivity.OpenXmlPowerTools;
using Codeuctivity.OpenXmlPowerTools.OpenXMLWordprocessingMLToHtmlConverter;
using SkiaSharp;
using Xunit;

namespace Codeuctivity.Tests
{
public class ImageHandlerTests
{
[Theory]
[InlineData(SKEncodedImageFormat.Png, "image/png")]
[InlineData(SKEncodedImageFormat.Jpeg, "image/jpeg")]
[InlineData(SKEncodedImageFormat.Webp, "image/webp")]
public void ShouldTransformImagesToDataUri(SKEncodedImageFormat format, string mime)
{
using var surface = SKSurface.Create(new SKImageInfo(10, 10));
surface.Canvas.Clear(SKColors.Red);
using var image = surface.Snapshot();
using var ms = new MemoryStream();
using (var data = image.Encode(format, 100))
{
data.SaveTo(ms);
}
ms.Position = 0;
var info = new ImageInfo { Image = ms, AltText = "alt", ImgStyleAttribute = new XAttribute(NoNamespace.style, "width:10px") };
var handler = new ImageHandler();
var result = handler.TransformImage(info);
var srcAttr = result.Attribute(NoNamespace.src);
Assert.NotNull(srcAttr);
Assert.StartsWith($"data:{mime};base64,", srcAttr.Value);
Assert.Equal("width:10px", result.Attribute(NoNamespace.style)?.Value);
}

[Fact]
public void ShouldTransformGifToDataUri()
{
var gif = System.Convert.FromBase64String("R0lGODlhAQABAPAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==");
using var ms = new MemoryStream(gif);
var info = new ImageInfo { Image = ms };
var handler = new ImageHandler();
var result = handler.TransformImage(info);
var srcAttr = result.Attribute(NoNamespace.src);
Assert.NotNull(srcAttr);
Assert.StartsWith("data:image/gif;base64,", srcAttr.Value);
}

[Fact]
public void ShouldThrowOnInvalidImage()
{
using var ms = new MemoryStream(new byte[] { 1, 2, 3, 4 });
var handler = new ImageHandler();
Assert.ThrowsAny<System.Exception>(() => handler.TransformImage(new ImageInfo { Image = ms }));
}

[Fact]
public void ShouldIncludeAltText()
{
using var surface = SKSurface.Create(new SKImageInfo(5, 5));
surface.Canvas.Clear(SKColors.Blue);
using var image = surface.Snapshot();
using var ms = new MemoryStream();
using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
{
data.SaveTo(ms);
}
ms.Position = 0;
var info = new ImageInfo { Image = ms, AltText = "demo" };
var handler = new ImageHandler();
var result = handler.TransformImage(info);
Assert.Equal("demo", result.Attribute(NoNamespace.alt)?.Value);
}

[Fact]
public void ShouldOmitAltTextWhenNotProvided()
{
using var surface = SKSurface.Create(new SKImageInfo(5, 5));
surface.Canvas.Clear(SKColors.Blue);
using var image = surface.Snapshot();
using var ms = new MemoryStream();
using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
{
data.SaveTo(ms);
}
ms.Position = 0;
var info = new ImageInfo { Image = ms };
var handler = new ImageHandler();
var result = handler.TransformImage(info);
Assert.Null(result.Attribute(NoNamespace.alt));
}
}
}
1 change: 1 addition & 0 deletions OpenXmlPowerTools.Tests/OpenXmlPowerTools.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="Codeuctivity.HtmlRenderer" Version="4.0.438" />
<PackageReference Include="Codeuctivity.SkiaSharpCompare" Version="3.0.165-PreRelease" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.0" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
34 changes: 28 additions & 6 deletions OpenXmlPowerTools/ColorParser.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
using SixLabors.ImageSharp;
using SkiaSharp;
using System;
using System.Drawing;

Copilot AI Aug 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a dependency on System.Drawing may introduce platform compatibility issues, as System.Drawing is Windows-specific and not recommended for cross-platform applications. Consider using a more portable color parsing solution or implementing custom color name parsing.

Suggested change

Copilot uses AI. Check for mistakes.
namespace Codeuctivity.OpenXmlPowerTools
{
public static class ColorParser
{
public static Color FromName(string name)
public static SKColor FromName(string name)
{
return Color.Parse(name);
if (!TryFromName(name, out var color))
{
throw new ArgumentException("Invalid color name", nameof(name));
}
return color;
}

public static bool TryFromName(string? name, out Color color)
public static bool TryFromName(string? name, out SKColor color)
{
return Color.TryParse(name, out color);
if (string.IsNullOrWhiteSpace(name))
{
color = default;
return false;
}

try
{
var drawingColor = ColorTranslator.FromHtml(name);
color = new SKColor(drawingColor.R, drawingColor.G, drawingColor.B, drawingColor.A);
return true;
}
catch
{
color = default;
return false;
}
}

public static bool IsValidName(string name)
{
return TryFromName(name, out _);
}
}
}
}
Loading
Loading