Skip to content

Commit

Permalink
New output size: payment part only
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelbl committed May 31, 2024
1 parent daec1ce commit 8096a98
Show file tree
Hide file tree
Showing 9 changed files with 390 additions and 12 deletions.
17 changes: 12 additions & 5 deletions Core/BillLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ internal class BillLayout
private double _textAscender;
private double _lineSpacing;
private double _extraSpacing;
private readonly double _paymentPartHoriOffset;


internal BillLayout(Bill bill, ICanvas graphics)
Expand All @@ -76,6 +77,7 @@ internal BillLayout(Bill bill, ICanvas graphics)
_formatter = new BillTextFormatter(bill, true);
_additionalLeftMargin = Math.Min(Math.Max(bill.Format.MarginLeft, 5.0), 12.0) - Margin;
_additionalRightMargin = Math.Min(Math.Max(bill.Format.MarginRight, 5.0), 12.0) - Margin;
_paymentPartHoriOffset = bill.Format.OutputSize == OutputSize.PaymentPartOnly ? 0 : ReceiptWidth;
}

internal void Draw()
Expand Down Expand Up @@ -106,6 +108,11 @@ internal void Draw()
}
DrawPaymentPart();

if (_bill.Format.OutputSize == OutputSize.PaymentPartOnly)
{
return;
}

// receipt

const int rcLabelPrefFontSize = 6; // pt
Expand Down Expand Up @@ -139,12 +146,12 @@ private void DrawPaymentPart()
const double qrCodeBottom = 42; // mm

// title section
_graphics.SetTransformation(ReceiptWidth + Margin, 0, 0, 1, 1);
_graphics.SetTransformation(_paymentPartHoriOffset + Margin, 0, 0, 1, 1);
_yPos = SlipHeight - Margin - _graphics.Ascender(FontSizeTitle);
_graphics.PutText(GetText(MultilingualText.KeyPaymentPart), 0, _yPos, FontSizeTitle, true);

// Swiss QR code section
_qrCode.Draw(_graphics, ReceiptWidth + Margin, qrCodeBottom);
_qrCode.Draw(_graphics, _paymentPartHoriOffset + Margin, qrCodeBottom);

// amount section
DrawPaymentPartAmountSection();
Expand All @@ -162,7 +169,7 @@ private void DrawPaymentPartAmountSection()
const double amountBoxWidthPp = 40; // mm
const double amountBoxHeightPp = 15; // mm

_graphics.SetTransformation(ReceiptWidth + Margin, 0, 0, 1, 1);
_graphics.SetTransformation(_paymentPartHoriOffset + Margin, 0, 0, 1, 1);

// currency
var y = AmountSectionTop - _labelAscender;
Expand Down Expand Up @@ -191,7 +198,7 @@ private void DrawPaymentPartAmountSection()

private void DrawPaymentPartInformationSection()
{
_graphics.SetTransformation(SlipWidth - PpInfoSectionWidth - Margin, 0, 0, 1, 1);
_graphics.SetTransformation(_paymentPartHoriOffset + PpAmountSectionWidth + 2 * Margin, 0, 0, 1, 1);
_yPos = SlipHeight - Margin - _labelAscender;

// account and creditor
Expand Down Expand Up @@ -234,7 +241,7 @@ private void DrawFurtherInformationSection()
return;
}

_graphics.SetTransformation(ReceiptWidth + Margin, 0, 0, 1, 1);
_graphics.SetTransformation(_paymentPartHoriOffset + Margin, 0, 0, 1, 1);
var y = furtherInformationSectionTop - _graphics.Ascender(fontSize);
var maxWidth = PaymentPartWidth - 2 * Margin - _additionalRightMargin;

Expand Down
17 changes: 14 additions & 3 deletions Core/OutputSize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// https://opensource.org/licenses/MIT
//

using System.Drawing;

namespace Codecrete.SwissQRBill.Generator
{
/// <summary>
Expand All @@ -14,7 +16,7 @@ namespace Codecrete.SwissQRBill.Generator
public enum OutputSize
{
/// <summary>
/// QR bill only (105 by 210 mm).
/// QR bill only (210 by 105 mm).
/// <para>
/// This size is suitable if the QR bill has no horizontal line.
/// If the horizontal line is needed and the A4 sheet size is not
Expand All @@ -31,7 +33,7 @@ public enum OutputSize
/// </summary>
QrCodeOnly,
/// <summary>
/// QR bill only with additional space at the top for the horizontal line (about 110 by 210 mm).
/// QR bill only with additional space at the top for the horizontal line (about 210 by 110 mm).
/// <para>
/// The extra 5 mm at the top create space for the horizontal line and
/// optionally for the scissors.
Expand All @@ -44,6 +46,15 @@ public enum OutputSize
/// This format applies a white background (as opposed to a transparent one).
/// </para>
/// </summary>
QrCodeWithQuietZone
QrCodeWithQuietZone,
/// <summary>
/// Payment part only (about 148 by 105 mm).
/// <para>
/// This size does not include separator lines. It is suitable for displaying the QR bill in online channels.
/// See <i>Implementation Guidelines QR Bill v2.3</i>, ch. <i>3.8 Layout rules for the online use of the QR-bill</i>
/// for additional requirements when using this size.
/// </para>
/// </summary>
PaymentPartOnly
}
}
2 changes: 0 additions & 2 deletions Core/Payments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
//

using System;
using System.ComponentModel;
using System.Text;
using static System.Net.Mime.MediaTypeNames;

namespace Codecrete.SwissQRBill.Generator
{
Expand Down
18 changes: 18 additions & 0 deletions Core/QRBill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ public static class QRBill
/// <value>The height, in mm.</value>
public const double QrCodeWithQuietZoneHeight = 56;

/// <summary>
/// The width of the payment part, in mm.
/// <seealso cref="OutputSize.PaymentPartOnly"/>
/// </summary>
/// <value>The width, in mm.</value>
public const double PaymentPartWidth = 148;

/// <summary>
/// The height of the payment part, in mm.
/// <seealso cref="OutputSize.PaymentPartOnly"/>
/// </summary>
/// <value>The height, in mm.</value>
public const double PaymentPartHeight = 105;


/// <summary>
/// Validates and cleans the bill data.
Expand Down Expand Up @@ -317,6 +331,10 @@ private static ICanvas CreateCanvas(BillFormat format)
drawingWidth = QrBillWithHoriLineWidth;
drawingHeight = QrBillWithHoriLineHeight;
break;
case OutputSize.PaymentPartOnly:
drawingWidth = PaymentPartWidth;
drawingHeight = PaymentPartHeight;
break;
case OutputSize.QrCodeOnly:
drawingWidth = QrCodeWidth;
drawingHeight = QrCodeHeight;
Expand Down
52 changes: 51 additions & 1 deletion CoreTest/PaymentCharacterSetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,33 @@ public void DecomposedAccents_AreComposed()
[InlineData("xƉx", "x.x")]
[InlineData("x\uD83C\uDDE8\uD83C\uDDEDx", "x.x")]
[InlineData("DŽ", "DZ")]
public void InvalidCharacters_AreReplaced(string text, string expectedResult)
public void InvalidExtendedLatinCharacters_AreReplaced(string text, string expectedResult)
{
Assert.Equal(expectedResult, Payments.CleanedAndTrimmedText(text, SpsCharacterSet.ExtendedLatin));
}

[Theory]
[InlineData("ab\nc", "ab c")]
[InlineData("ÿÝ", "yY")]
[InlineData("", "E")]
[InlineData("¥", "Y")]
[InlineData("ȆȇȈȉ", "EeIi")]
[InlineData("Ǒ", "O")]
[InlineData("ljnjffi", "ljnjffi")]
[InlineData("Ǽ", "AE")]
[InlineData("ʷ", "w")]
[InlineData("", "5")]
[InlineData("", "a/s")]
[InlineData("", "VII")]
[InlineData("", "3")]
[InlineData("xƉx", "x.x")]
[InlineData("x\uD83C\uDDE8\uD83C\uDDEDx", "x.x")]
[InlineData("DŽ", "DZ")]
public void InvalidLatin1SubsetCharacters_AreReplaced(string text, string expectedResult)
{
Assert.Equal(expectedResult, Payments.CleanedAndTrimmedText(text, SpsCharacterSet.Latin1Subset));
}

[Fact]
public void CleanedNull_ReturnsNull()
{
Expand Down Expand Up @@ -131,6 +153,34 @@ public void AllChars_HaveGoodReplacement(char ch)
Assert.True(Payments.IsValidText(cleaned, SpsCharacterSet.ExtendedLatin));
}

[Fact]
public void CleanValueWithValidChars_ReturnsFalse()
{
Payments.CleanValue("def", out var cleaningResult);
Assert.False(cleaningResult.ReplacedUnsupportedChars);
Assert.Equal("def", cleaningResult.CleanedString);
}

[Theory]
[InlineData("ab\nc", "ab c")]
[InlineData("ȆȇȈȉ", "EeIi")]
[InlineData("Ǒ", "O")]
[InlineData("Ǽ", "AE")]
public void CleanValueWithInvalidChars_ReturnsTrue(string text, string expectedResult)
{
Payments.CleanValue(text, out var cleaningResult);
Assert.True(cleaningResult.ReplacedUnsupportedChars);
Assert.Equal(expectedResult, cleaningResult.CleanedString);
}

[Fact]
public void CleanValuleWithEmpty_ReturnsNull()
{
Payments.CleanValue("", out var cleaningResult);
Assert.False(cleaningResult.ReplacedUnsupportedChars);
Assert.Null(cleaningResult.CleanedString);
}

public class ExtendedLatinCharsProvider : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
Expand Down
36 changes: 36 additions & 0 deletions CoreTest/PaymentPartTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Swiss QR Bill Generator for .NET
// Copyright (c) 2024 Manuel Bleichenbacher
// Licensed under MIT License
// https://opensource.org/licenses/MIT
//

using Codecrete.SwissQRBill.Generator;
using System.Threading.Tasks;
using Xunit;

namespace Codecrete.SwissQRBill.CoreTest
{
public class PaymentPartTest
{
[Fact]
public Task CreateQrBill1()
{
Bill bill = SampleData.CreateExample1();
bill.Format.OutputSize = OutputSize.PaymentPartOnly;
bill.Format.GraphicsFormat = GraphicsFormat.SVG;
byte[] svg = QRBill.Generate(bill);
return VerifyImages.VerifySvg(svg);
}

[Fact]
public Task CreateQrBill2()
{
Bill bill = SampleData.CreateExample2();
bill.Format.OutputSize = OutputSize.PaymentPartOnly;
bill.Format.GraphicsFormat = GraphicsFormat.PDF;
byte[] pdf = QRBill.Generate(bill);
return VerifyImages.VerifyPdf(pdf);
}
}
}
1 change: 0 additions & 1 deletion CoreTest/QRBillTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

using Codecrete.SwissQRBill.Generator;
using System.Threading.Tasks;
using VerifyXunit;
using Xunit;


Expand Down
Loading

0 comments on commit 8096a98

Please sign in to comment.