From 4b8c2af1d471f97c0f270686d575c84f124a433a Mon Sep 17 00:00:00 2001 From: kenan-kajkus Date: Thu, 23 Nov 2023 23:43:39 +0100 Subject: [PATCH] Add reference and use string builder. --- GiroCode/GiroCodeGenerator.cs | 106 ++++++++++++++---------- GiroCode/IGiroCodeGenerator.cs | 4 +- GiroCodeTests/GiroCodeGeneratorTests.cs | 5 +- 3 files changed, 66 insertions(+), 49 deletions(-) diff --git a/GiroCode/GiroCodeGenerator.cs b/GiroCode/GiroCodeGenerator.cs index a7d341d..d076402 100644 --- a/GiroCode/GiroCodeGenerator.cs +++ b/GiroCode/GiroCodeGenerator.cs @@ -6,10 +6,16 @@ namespace GiroCode; public class GiroCodeGenerator : IGiroCodeGenerator { - #region Fields + #region constants + + private const int StrokeWidth = 5; + private const int YOffset = 15; + private const string Br = "\n"; + + #endregion + + #region fields - private readonly int _strokeWidth = 5; - private readonly int _yOffset = 15; private readonly int _textSize = 20; private readonly string _giroCodeText = "Giro-Code"; @@ -29,7 +35,7 @@ public GiroCodeGenerator(string giroCodeText, int textSize) _giroCodeText = giroCodeText; _textSize = textSize; } - + /// /// Generates the giro code. /// @@ -38,6 +44,7 @@ public GiroCodeGenerator(string giroCodeText, int textSize) /// The remittance. /// The amount. /// The bic. + /// External reference /// The character set. /// giro code as byte array public byte[] GenerateGiroCode( @@ -45,7 +52,8 @@ public byte[] GenerateGiroCode( string iban, string remittance, decimal amount, - string bic, + string? bic, + string? reference = null, CharSet charSet = CharSet.Utf8) { byte[] qrCode; @@ -56,9 +64,9 @@ public byte[] GenerateGiroCode( throw new Exception("IbanIsEmpty"); var ibanNormalized = iban.Trim(); - var barcodeContent = GenerateQrCodeContent(beneficiary, ibanNormalized, remittance, amount, bic, charSet: charSet); + var barcodeContent = GenerateQrCodeContent(beneficiary, ibanNormalized, remittance, amount, bic, reference, charSet: charSet); if (!IsBarCodeValid(barcodeContent, charSet)) - throw new Exception("IbanIsInvalid"); + throw new Exception("Barcode exceeds binary size limit"); qrCode = GenerateQrCode(barcodeContent); } @@ -78,6 +86,7 @@ public byte[] GenerateGiroCode( /// The remittance. /// The amount. /// The bic. + /// The external reference /// The character set. /// qr code as string private string GenerateQrCodeContent( @@ -85,36 +94,41 @@ private string GenerateQrCodeContent( string iban, string remittance, decimal amount, - string bic = null, + string? bic = null, + string? reference = null, CharSet charSet = CharSet.Utf8) { - var barcodeContent = - /* Service tag */ - "BCD\r\n" + - /* Version */ - "002\r\n" + - /* Character set */ - $"{(int)charSet}\r\n" + - /* SEPA Credit Transfer */ - "SCT\r\n" + - /* Recipient's BIC */ - $"{bic}\r\n" + - /* Recipient's name */ - $"{beneficiary}\r\n" + - /* Recipient's IBAN */ - $"{iban}\r\n" + - /* Amount */ - $"EUR{amount.ToString("F", CultureInfo.InvariantCulture)}\r\n" + - /* Purpose, optional */ - "CHAR\r\n" + - /* Reference, optional */ - "\r\n" + - /* Remittance information */ - $"{remittance}\r\n" + - /* Hint */ - string.Empty; - - return barcodeContent; + var barcodeContentBuilder = new StringBuilder(); + + // service tag + barcodeContentBuilder.Append("BCD").Append(Br); + // version + barcodeContentBuilder.Append("002").Append(Br); + // character set + barcodeContentBuilder.Append((int)charSet).Append(Br); + // SEPA Credit Transfer + barcodeContentBuilder.Append("SCT").Append(Br); + // Recipient's BIC + barcodeContentBuilder.Append(bic).Append(Br); + /* Recipient's name */ + barcodeContentBuilder.Append(beneficiary).Append(Br); + /* Recipient's IBAN */ + barcodeContentBuilder.Append(iban).Append(Br); + /* Amount */ + barcodeContentBuilder + .Append("EUR") + .Append(amount.ToString("F", CultureInfo.InvariantCulture)) + .Append(Br); + /* Purpose, optional */ + barcodeContentBuilder.Append("CHAR").Append(Br); + /* Reference, optional */ + barcodeContentBuilder.Append(reference).Append(Br); + /* Remittance information */ + barcodeContentBuilder.Append(remittance).Append(Br); + /* Hint */ + barcodeContentBuilder.Append(string.Empty); + + return barcodeContentBuilder.ToString(); } /// @@ -129,18 +143,18 @@ private byte[] GenerateQrCode(string barcodeContent) // ECC level needs to be "M" per specification var qrCodeData = qrGenerator.CreateQrCode(barcodeContent, QRCodeGenerator.ECCLevel.M); var qrCode = new PngByteQRCode(qrCodeData); - var qrCodeImage = qrCode.GetGraphic(_strokeWidth); + var qrCodeImage = qrCode.GetGraphic(StrokeWidth); var qrBitmap = SKBitmap.FromImage(SKImage.FromBitmap(SKBitmap.Decode(qrCodeImage))); // Generate canvas - var canvasBitmap = new SKBitmap(qrBitmap.Width, qrBitmap.Height + _yOffset); + var canvasBitmap = new SKBitmap(qrBitmap.Width, qrBitmap.Height + YOffset); var canvas = new SKCanvas(canvasBitmap); // Set whole canvas white canvas.DrawColor(SKColors.White); // Text settings - var textPoint = new SKPoint(qrBitmap.Width / 2f, _yOffset - _strokeWidth + (_textSize / 2)); + var textPoint = new SKPoint(qrBitmap.Width / 2f, YOffset - StrokeWidth + (_textSize / 2f)); var textPaint = new SKPaint { TextSize = _textSize, @@ -155,7 +169,7 @@ private byte[] GenerateQrCode(string barcodeContent) SKFontStyleSlant.Italic) }; - var qrPoint = new SKPoint(0f, _yOffset); + var qrPoint = new SKPoint(0f, YOffset); canvas.DrawBitmap(qrBitmap, qrPoint); @@ -163,15 +177,15 @@ private byte[] GenerateQrCode(string barcodeContent) var framePaint = new SKPaint { Style = SKPaintStyle.Stroke, - StrokeWidth = _strokeWidth, + StrokeWidth = StrokeWidth, Color = SKColors.Black, IsAntialias = true }; canvas.DrawRoundRect( - _strokeWidth / 2f, - _yOffset - (_strokeWidth / 2f), - qrBitmap.Width - _strokeWidth, + StrokeWidth / 2f, + YOffset - (StrokeWidth / 2f), + qrBitmap.Width - StrokeWidth, qrBitmap.Height, 20f, 20f, @@ -180,8 +194,8 @@ private byte[] GenerateQrCode(string barcodeContent) var textBounds = default(SKRect); _ = textPaint.MeasureText(_giroCodeText, ref textBounds); - textBounds.Left -= _strokeWidth; - textBounds.Right += _strokeWidth; + textBounds.Left -= StrokeWidth; + textBounds.Right += StrokeWidth; textBounds.Location = new SKPoint(textPoint.X - (textBounds.Width / 2), textPoint.Y - textBounds.Height); canvas.ClipRect(textBounds); @@ -248,7 +262,7 @@ private bool IsBarCodeValid(string barcodeContent, CharSet charSet) /// /// The encoding. /// The message. - /// bitman as byte array + /// bitmap as byte array private byte[] GetPayloadBytes(string encoding, string message) { var iso = Encoding.GetEncoding(encoding); diff --git a/GiroCode/IGiroCodeGenerator.cs b/GiroCode/IGiroCodeGenerator.cs index 6089689..0f4d535 100644 --- a/GiroCode/IGiroCodeGenerator.cs +++ b/GiroCode/IGiroCodeGenerator.cs @@ -10,6 +10,7 @@ public interface IGiroCodeGenerator /// The remittance. /// The amount. /// The bic. + /// The external reference /// The character set. /// giro code as byte array byte[] GenerateGiroCode( @@ -17,6 +18,7 @@ byte[] GenerateGiroCode( string iban, string remittance, decimal amount, - string bic, + string? bic, + string? reference, CharSet charSet = CharSet.Utf8); } \ No newline at end of file diff --git a/GiroCodeTests/GiroCodeGeneratorTests.cs b/GiroCodeTests/GiroCodeGeneratorTests.cs index 7d11251..4d3387f 100644 --- a/GiroCodeTests/GiroCodeGeneratorTests.cs +++ b/GiroCodeTests/GiroCodeGeneratorTests.cs @@ -46,7 +46,7 @@ public async Task GenerateGiroCode() } [Fact] - public async Task GenerateGiroCoded_ShouldNotSuccess_WhenExceeds331Bytes() + public void GenerateGiroCoded_ShouldNotSuccess_WhenExceeds331Bytes() { var rem = ""; for (var i = 0; i < 180; i++) @@ -59,7 +59,8 @@ public async Task GenerateGiroCoded_ShouldNotSuccess_WhenExceeds331Bytes() "DE74500105176879856947", rem, 1.44M, - "INGDDEFFXXX"); + "INGDDEFFXXX", + "reference"); var exceptionAssertions = a.Should().Throw(); exceptionAssertions.WithMessage("QrCodeCreationFailed");