|
- using System.Drawing;
- using DebenuPDFLibraryDLL1013;
- using EnvelopeRenderer.Cli.Render;
-
- #pragma warning disable CA1416
-
- namespace EnvelopeRenderer.Cli.Tests;
-
- /// <summary>A <text> element with <c>fill</c> is a filled rectangle (cover-up) that its own text,
- /// or any element with a higher render order, draws over.</summary>
- public class FilledRectangleTests
- {
- private static string WriteTemplate(string xml)
- {
- var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xml");
- File.WriteAllText(path, xml);
- return path;
- }
-
- private static string DllPath() => Path.Combine(
- AppContext.BaseDirectory,
- Environment.Is64BitProcess ? "DebenuPDFLibrary64DLL1013.dll" : "DebenuPDFLibraryDLL1013.dll");
-
- [Fact]
- public void Parse_FillWithBoxAndNoText_IsAPureRectangle()
- {
- var path = WriteTemplate("""
- <envelopeTemplate pageWidth="297" pageHeight="684">
- <text x="10" y="20" font="Arial" size="12" width="100" height="40" fill="#FFFFFF" />
- </envelopeTemplate>
- """);
-
- var result = TemplateXmlParser.Parse(path);
-
- Assert.True(result.Succeeded, string.Join("; ", result.Errors));
- var element = Assert.Single(result.Document!.Elements);
- Assert.True(element.HasFill);
- Assert.Equal(new PdfColor(255, 255, 255), element.FillColor);
- Assert.Equal(string.Empty, element.Runs[0].Literal);
- }
-
- [Theory]
- [InlineData("""<text x="1" y="2" font="Arial" size="12" fill="#FFFFFF">Hi</text>""", "'width' and 'height'")]
- [InlineData("""<text x="1" y="2" font="Arial" size="12" width="5" height="5" fill="nope">Hi</text>""", "'fill'")]
- [InlineData("""<text x="1" y="2" font="Arial" size="12" width="5" height="5" fill="#000000" angle="10">Hi</text>""", "rotated")]
- public void Parse_InvalidFill_Fails(string textXml, string expected)
- {
- var result = TemplateXmlParser.Parse(WriteTemplate(
- $"""<envelopeTemplate pageWidth="297" pageHeight="684">{textXml}</envelopeTemplate>"""));
-
- Assert.False(result.Succeeded);
- Assert.Contains(result.Errors, e => e.Contains(expected));
- }
-
- [Fact]
- public void Render_FillElement_ProducesDrawWithFillColorAndBox_InRenderOrder()
- {
- var elements = new[]
- {
- new TemplateElement(10, 20, "Arial", 12, new[] { new TemplateTextRun(string.Empty, null) },
- Width: 100, Height: 40, FillColor: new PdfColor(255, 255, 255)),
- TemplateElement.Static(12, 30, "Arial", 12, "over") with { RenderOrder = 1 },
- };
- var template = new TemplateDocument(297, 684, elements, Array.Empty<TemplateAddressControl>());
- var draws = new List<IReadOnlyList<TextDraw>>();
- var renderer = new CapturingRenderer(draws);
-
- var result = RenderEngine.Render(
- template, new[] { "A" },
- new List<IReadOnlyDictionary<string, string>> { new Dictionary<string, string> { ["A"] = "1" } },
- renderer, "out.pdf");
-
- Assert.True(result.Succeeded, string.Join("; ", result.Errors));
- Assert.Equal(new PdfColor(255, 255, 255), draws[0][0].FillColor);
- Assert.Equal(100, draws[0][0].Width);
- Assert.Equal("over", draws[0][1].Text);
- Assert.Null(draws[0][1].FillColor);
- }
-
- private sealed class CapturingRenderer : IPdfRenderer
- {
- private readonly List<IReadOnlyList<TextDraw>> _draws;
- public CapturingRenderer(List<IReadOnlyList<TextDraw>> draws) => _draws = draws;
-
- public bool AddPage(double w, double h, IReadOnlyList<TextDraw> draws, PageBackground? background, out string? error)
- {
- _draws.Add(draws);
- error = null;
- return true;
- }
-
- public bool Save(string outputPath, out string? error)
- {
- error = null;
- return true;
- }
-
- public void Dispose() { }
- }
-
- [Fact]
- public void RealRender_FilledRectangleCoversBackground_AndTextDrawsOverIt()
- {
- var key = DebenuLicenseKey.Resolve();
- if (key is null)
- {
- Console.WriteLine("SKIPPED: no Debenu license key available locally.");
- return;
- }
-
- var dir = Path.Combine(Path.GetTempPath(), $"rect-{Guid.NewGuid():N}");
- Directory.CreateDirectory(dir);
- try
- {
- var imagePath = Path.Combine(dir, "red.png");
- using (var bitmap = new Bitmap(40, 40))
- {
- using var g = Graphics.FromImage(bitmap);
- g.Clear(Color.Red);
- bitmap.Save(imagePath, System.Drawing.Imaging.ImageFormat.Png);
- }
-
- // 200x200 page, red background; a white 100x100 rectangle at (50,50) with black text in it.
- var rectangle = new TemplateElement(50, 50, "Arial", 40,
- new[] { new TemplateTextRun("MMMM", null) },
- Width: 100, Height: 100, FillColor: new PdfColor(255, 255, 255));
- var template = new TemplateDocument(200, 200, new[] { rectangle },
- Array.Empty<TemplateAddressControl>(),
- new TemplateBackground("red.png", null, 1, null, dir));
- var output = Path.Combine(dir, "out.pdf");
-
- Assert.True(DebenuPdfRenderer.TryCreate(DllPath(), key, out var renderer, out var error), error);
- using (renderer)
- {
- var result = RenderEngine.Render(
- template, new[] { "A" },
- new List<IReadOnlyDictionary<string, string>> { new Dictionary<string, string> { ["A"] = "1" } },
- renderer!, output);
- Assert.True(result.Succeeded, string.Join("; ", result.Errors));
- }
-
- var pdf = new PDFLibrary(DllPath());
- pdf.UnlockKey(key);
- Assert.NotEqual(0, pdf.LoadFromFile(output, ""));
- var png = Path.Combine(dir, "page.png");
- Assert.NotEqual(0, pdf.RenderPageToFile(72, 1, 0, png));
- pdf.ReleaseLibrary();
-
- using var rendered = new Bitmap(png);
- var corner = rendered.GetPixel(5, 5);
- Assert.True(corner.R > 200 && corner.G < 60, $"outside the rectangle the red background stays: {corner}");
-
- // Inside the rectangle (PDF y=50..150 is image rows 50..150): white except where text draws.
- int white = 0, dark = 0, red = 0;
- for (var x = 55; x < 145; x++)
- {
- for (var y = 55; y < 145; y++)
- {
- var c = rendered.GetPixel(x, y);
- if (c.R > 230 && c.G > 230 && c.B > 230) { white++; }
- else if (c.R < 90 && c.G < 90 && c.B < 90) { dark++; }
- else if (c.R > 200 && c.G < 60 && c.B < 60) { red++; }
- }
- }
-
- Assert.Equal(0, red);
- Assert.True(white > 3000, $"the rectangle should be mostly white: {white}");
- Assert.True(dark > 100, $"text should draw over the rectangle: {dark}");
- }
- finally
- {
- Directory.Delete(dir, recursive: true);
- }
- }
- }
|