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
7 changes: 6 additions & 1 deletion API.Tests/Controllers/PdfControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public void ReadFields_ReturnsFieldsResponse_WhenSuccessful()
var fileMock = new Mock<IFormFile>();
var fields = new List<PdfField> { new TestPdfField("Field1", "Text", 1, false, "FieldValue1") };
var pages = new List<PdfPageInfo> { new TestPdfPageInfo(1, 600f, 800f) };
_pdfServiceMock.Setup(service => service.ReadFields(It.IsAny<IFormFile>())).Returns((fields, pages));
var fonts = new List<string> { "arial-mt", "arial-boldmt", "arial-italicmt", "arial-bolditalicmt" };
_pdfServiceMock.Setup(service => service.ReadFields(It.IsAny<IFormFile>())).Returns((fields, pages, fonts));

// Act
var result = _controller.ReadFields(fileMock.Object);
Expand All @@ -96,6 +97,10 @@ public void ReadFields_ReturnsFieldsResponse_WhenSuccessful()
Assert.NotNull(response.Pages);
Assert.Single(response.Pages);
Assert.Equal(1, response.Pages[0].Number);

Assert.NotNull(response.Fonts);
Assert.Equal(4, response.Fonts.Count);
Assert.Equal("arial-mt", response.Fonts[0]);
}

[Fact(DisplayName = "ReadFields returns BadRequest when any exception thrown")]
Expand Down
45 changes: 45 additions & 0 deletions API.Tests/Converters/EnumJsonConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using API.Converters;
using System.Text.Json;
using static API.Models.Requests.FillRequest;

namespace API.Tests.Converters
{
public class EnumJsonConverterTests
{
private readonly JsonSerializerOptions _options;

public EnumJsonConverterTests()
{
_options = new JsonSerializerOptions
{
Converters = { new EnumJsonConverter<TextHorizontalAlignment>() }
};
}

[Theory(DisplayName = "Read single value returns expected value")]
[InlineData("\"left\"", TextHorizontalAlignment.LEFT)]
[InlineData("\"CENTER\"", TextHorizontalAlignment.CENTER)]
public void Read_SingleValue_ReturnsExpectedValue(string json, TextHorizontalAlignment expected)
{
var result = JsonSerializer.Deserialize<TextHorizontalAlignment>(json, _options);
Assert.Equal(expected, result);
}

[Fact(DisplayName = "Read invalid value throws JsonException")]
public void Read_InvalidArray_ThrowsJsonException()
{
var json = "\"somevalue\"";

Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<TextHorizontalAlignment>(json, _options));
}

[Theory(DisplayName = "Write Enum value serializes correctly")]
[InlineData(TextHorizontalAlignment.LEFT, "\"left\"")]
[InlineData(TextHorizontalAlignment.RIGHT, "\"right\"")]
public void Write_Value_SerializesCorrectly(TextHorizontalAlignment value, string result)
{
var json = JsonSerializer.Serialize(value, _options);
Assert.Equal(result, json);
}
}
}
16 changes: 6 additions & 10 deletions API.Tests/Converters/FieldValueJsonConverterTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@

using System;
using System.Collections.Generic;
using System.Text.Json;
using API.Converters;
using Xunit;
using System.Text.Json;

namespace API.Tests.Converters
{
Expand Down Expand Up @@ -73,14 +69,14 @@ public void Read_InvalidArray_ThrowsJsonException()
}

[Theory(DisplayName = "Write single value serializes correctly")]
[InlineData("stringValue", "stringValue")]
[InlineData(123, 123)]
[InlineData(true, true)]
[InlineData(false, false)]
[InlineData("stringValue", "\"stringValue\"")]
[InlineData(123, "123")]
[InlineData(true, "true")]
[InlineData(false, "false")]
public void Write_Value_SerializesCorrectly(object value, object result)
{
var json = JsonSerializer.Serialize(value, _options);
Assert.Equal(value, result);
Assert.Equal(result, json);
}

[Fact(DisplayName = "Write string array serializes correctly")]
Expand Down
148 changes: 146 additions & 2 deletions API.Tests/Services/ITextPdfService/Functional/PdfServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using iText.Forms;
using iText.Forms.Fields;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas.Parser;
using iText.Kernel.Pdf.Canvas.Parser.Listener;
using iText.Kernel.Pdf.Xobject;
using Microsoft.AspNetCore.Http;
using Moq;
Expand All @@ -17,7 +19,7 @@ public class PdfServiceTest

public PdfServiceTest()
{
_pdfService = new PdfService();
_pdfService = new PdfService(null);
}

[Fact(DisplayName = "ReadFields should return fields from read PDF")]
Expand Down Expand Up @@ -65,6 +67,22 @@ public void ReadFields_ShouldReturnPagesInfo()
Assert.True(pageInfo.Height > 0);
}

[Fact(DisplayName = "ReadFields should return a list of all available fonts from read PDF")]
public void ReadFields_ShouldReturnAvailableFontsList()
{
// Arrange
var pdfFile = CreateSimplePdfForm(new List<FillRequest.Field>());

// Act
var fonts = _pdfService.ReadFields(pdfFile.Object).fonts;

// Assert
Assert.NotNull(fonts);
Assert.NotEmpty(fonts);
Assert.Contains("courier", fonts);//One of the fonts automatically registered in the itext library
Assert.Contains("courier-bold", fonts);//One of the fonts automatically registered in the itext library
}

[Fact(DisplayName = "Fill should fill fields in real PDF")]
public void Fill_ShouldFillFields()
{
Expand Down Expand Up @@ -158,7 +176,7 @@ public void Fill_ShouldAddImage()
Assert.Equal("png", img.IdentifyImageFileExtension());
}

[Fact(DisplayName = "Fill should return an error if the scale is less than or equal to 0 or the width or height of the image after applying the scale or width and height, if specified, is greater than the width or height of the page.")]
[Fact(DisplayName = "Fill should return an error if the scale is less than or equal to 0, or if the image width or height after applying the scale, or the width and height if specified, is greater than the page width or height.")]
public void Fill_ShouldThrowError_WhenIncorrectImageSize()
{
// Arrange
Expand Down Expand Up @@ -202,6 +220,132 @@ public void Fill_ShouldThrowError_WhenIncorrectImageSize()
//Black rectangle 50x30 (.png)
private readonly string ImageBase64String = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAeCAIAAADhM9qrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAcSURBVFhH7cExAQAAAMKg9U9tDQ8gAAAAAK7UABGyAAFALqTGAAAAAElFTkSuQmCC";

[Fact(DisplayName = "Fill should add a text in a real PDF")]
public void Fill_ShouldAddText()
{
// Arrange
var pdfFile = CreateSimplePdfForm(new List<FillRequest.Field>());
var firstfont = _pdfService.ReadFields(pdfFile.Object).fonts[0];

pdfFile.Object.OpenReadStream().Position = 0;
var textToAdd = "Test text1";
var fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field
{
Page = 1,
Value = textToAdd,
TextStyle = new FillRequest.TextStyle
{
HorizontalAlignment = FillRequest.TextHorizontalAlignment.CENTER,
VerticalAlignment = FillRequest.TextVerticalAlignment.MIDDLE,
Color = "#00ff00",
Font = new FillRequest.FontStyle { Name = firstfont, Size = 8 }
}
}
};

// Act & Assert
var filledPdfBytes = _pdfService.Fill(pdfFile.Object, fieldsToFill);
Assert.NotNull(filledPdfBytes);

using var pdfDocument = new PdfDocument(new PdfReader(new MemoryStream(filledPdfBytes)));

var docText = PdfTextExtractor.GetTextFromPage(pdfDocument.GetPage(1), new SimpleTextExtractionStrategy());
Assert.Equal(textToAdd, docText);
}

[Fact(DisplayName = "Fill should return an error if the x, y coordinates, or the width or height of the text box, if specified, are greater than the width or height of the page.")]
public void Fill_ShouldThrowError_WhenIncorrectTextFieldPositionOrSize()
{
// Arrange
var pdfFile = CreateSimplePdfForm(new List<FillRequest.Field>());

var firstPage = _pdfService.ReadFields(pdfFile.Object).pages[0];
var pageWidth = firstPage.Width;
var pageHeight = firstPage.Height;

// Arrange
pdfFile.Object.OpenReadStream().Position = 0;
var fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field { Value = "Test text1", X = pageWidth + 50 , Y = pageHeight + 100 }
};

// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => _pdfService.Fill(pdfFile.Object, fieldsToFill));
Assert.Contains("Invalid text coordinate X value", exception.Message);

// Arrange
pdfFile.Object.OpenReadStream().Position = 0;
fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field { Value = "Test text1", Y = pageHeight + 100 }
};

// Act & Assert
exception = Assert.Throws<ArgumentException>(() => _pdfService.Fill(pdfFile.Object, fieldsToFill));
Assert.Contains("Invalid text coordinate Y value", exception.Message);

// Arrange
pdfFile.Object.OpenReadStream().Position = 0;
fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field { Value = "Test text1", Width = pageWidth + 50 , Height = pageHeight + 100 }
};

// Act & Assert
exception = Assert.Throws<ArgumentException>(() => _pdfService.Fill(pdfFile.Object, fieldsToFill));
Assert.Contains("Invalid text width value", exception.Message);

// Arrange
pdfFile.Object.OpenReadStream().Position = 0;
fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field { Value = "Test text1", Height = pageHeight + 100 }
};

// Act & Assert
exception = Assert.Throws<ArgumentException>(() => _pdfService.Fill(pdfFile.Object, fieldsToFill));
Assert.Contains("Invalid text height value", exception.Message);
}

[Fact(DisplayName = "Fill should return an error if the font name is unknown.")]
public void Fill_ShouldThrowError_WhenUnknownFontName()
{
// Arrange
var pdfFile = CreateSimplePdfForm(new List<FillRequest.Field>());

var fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field
{
Value = "Test text1",
TextStyle = new FillRequest.TextStyle
{
Font = new FillRequest.FontStyle { Name = "UnknownFont" }
}
}
};

// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => _pdfService.Fill(pdfFile.Object, fieldsToFill));
Assert.Contains("The font with the name 'UnknownFont' not found", exception.Message);
}

[Fact(DisplayName = "Fill should return an error if the text color value is invalid.")]
public void Fill_ShouldThrowError_WhenInvalidColorValue()
{
// Arrange
var pdfFile = CreateSimplePdfForm(new List<FillRequest.Field>());

var fieldsToFill = new List<FillRequest.Field>{
new FillRequest.Field
{
Value = "Test text1",
TextStyle = new FillRequest.TextStyle { Color = "Unknown color" }
}
};

// Act & Assert
var exception = Assert.Throws<ArgumentException>(() => _pdfService.Fill(pdfFile.Object, fieldsToFill));
Assert.Contains("Unknown color value", exception.Message);
}

private Mock<IFormFile> CreateSimplePdfForm(List<FillRequest.Field> fields)
{
var stream = new MemoryStream();
Expand Down
3 changes: 2 additions & 1 deletion API/Controllers/PdfController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public IActionResult ReadFields(IFormFile file)
{
try
{
var (fields, pages) = _pdfService.ReadFields(file);
var (fields, pages, fonts) = _pdfService.ReadFields(file);

return Ok(new FieldsResponse()
{
Expand All @@ -66,6 +66,7 @@ public IActionResult ReadFields(IFormFile file)
Width = p.Width,
Height = p.Height
}).ToList(),
Fonts = fonts,
Fields = fields.Select(f => GetResponseField(f)).ToList()
});
}
Expand Down
28 changes: 28 additions & 0 deletions API/Converters/EnumJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace API.Converters;

public class EnumJsonConverter<TEnum> : JsonConverter<TEnum>
where TEnum : Enum
{
public override TEnum? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
var stringValue = reader.GetString();

if (!Enum.TryParse(typeof(TEnum), stringValue, true, out object? parsedEnum))
throw new JsonException($"Unexpected enum value: '{stringValue}'. Expected one of the values:{string.Join(", ", Enum.GetNames(typeof(TEnum)))}.");

return (TEnum)parsedEnum;
}

throw new JsonException($"Unexpected token type: '{reader.TokenType}'. Expected string type.");
}

public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString().ToLower());
}
}
9 changes: 9 additions & 0 deletions API/Converters/FieldValueJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp
case List<int> intList:
JsonSerializer.Serialize(writer, intList, options);
break;
case int intValue:
writer.WriteNumberValue(intValue);
break;
case double doubleValue:
writer.WriteNumberValue(doubleValue);
break;
case bool boolValue:
writer.WriteBooleanValue(boolValue);
break;
default:
writer.WriteStringValue(value.ToString());
break;
Expand Down
Loading