diff --git a/DocumentOperator.Infrastructure/Services/QrCodeProcessing/DevExpressSwissQrCodeProcessor.cs b/DocumentOperator.Infrastructure/Services/QrCodeProcessing/DevExpressSwissQrCodeProcessor.cs index e767283..73d26e0 100644 --- a/DocumentOperator.Infrastructure/Services/QrCodeProcessing/DevExpressSwissQrCodeProcessor.cs +++ b/DocumentOperator.Infrastructure/Services/QrCodeProcessing/DevExpressSwissQrCodeProcessor.cs @@ -1,9 +1,11 @@ using Codecrete.SwissQRBill.Generator; +using DevExpress.Drawing; using DevExpress.Pdf; using DocumentOperator.Application.Common.Interfaces; using DocumentOperator.Domain.Exceptions; using DocumentOperator.Domain.ValueObjects; using System.Drawing; +using System.Runtime.Versioning; using ZXing; namespace DocumentOperator.Infrastructure.Services.QrCodeProcessing; @@ -17,6 +19,7 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor private const int QrCodeSearchDpi = 300; // High DPI for better QR code recognition /// + [SupportedOSPlatform("windows")] public async Task ExtractSwissQrCodeAsync(byte[] pdfBytes, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(pdfBytes); @@ -64,30 +67,39 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor /// /// Renders a PDF page to a high-resolution bitmap for QR code detection /// - private static Bitmap RenderPageToImage(PdfDocumentProcessor processor, int pageIndex) + private static DXBitmap RenderPageToImage(PdfDocumentProcessor processor, int pageIndex) { // Render page at high DPI for better QR code recognition - var pageImage = processor.CreateBitmap(pageIndex + 1, QrCodeSearchDpi); + var pageImage = processor.CreateDXBitmap(pageIndex + 1, QrCodeSearchDpi); return pageImage; } /// - /// Decodes QR code from an image using ZXing library + /// Decodes QR code from a DXBitmap image using ZXing library /// - private static string? DecodeQrCodeFromImage(Bitmap image) + [SupportedOSPlatform("windows")] + private static string? DecodeQrCodeFromImage(DXBitmap dxImage) { + // Convert DXBitmap to System.Drawing.Bitmap via MemoryStream + using var ms = new MemoryStream(); + dxImage.Save(ms, DXImageFormat.Png); + ms.Position = 0; + + using var gdiImage = Image.FromStream(ms); + using var gdiBitmap = new Bitmap(gdiImage); + var reader = new ZXing.Windows.Compatibility.BarcodeReader { AutoRotate = true, - TryInverted = true, Options = new ZXing.Common.DecodingOptions { - PossibleFormats = new[] { BarcodeFormat.QR_CODE }, - TryHarder = true + PossibleFormats = [BarcodeFormat.QR_CODE], + TryHarder = true, + TryInverted = true } }; - var result = reader.Decode(image); + var result = reader.Decode(gdiBitmap); return result?.Text; } @@ -155,7 +167,13 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor } /// - /// Maps Codecrete Address to our AddressData value object + /// Maps Codecrete Address to our AddressData value object. + /// + /// NOTE: AddressLine1 and AddressLine2 (Combined Address / K-Type) are deprecated + /// as of Swiss Payment Standards 2025 (effective 21 Nov 2025). + /// The Swiss QR Bill now mandates Structured Address (S-Type) format. + /// These fields are retained for backward compatibility with legacy QR codes + /// generated before the deprecation date. /// private static AddressData MapAddress(Codecrete.SwissQRBill.Generator.Address address) { @@ -165,8 +183,10 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor Name = address.Name ?? string.Empty, Street = address.Street, BuildingNumber = address.HouseNo, +#pragma warning disable CS0618 // AddressLine1/AddressLine2 obsolete but required for backward compatibility AddressLine1 = address.AddressLine1, AddressLine2 = address.AddressLine2, +#pragma warning restore CS0618 PostalCode = address.PostalCode ?? string.Empty, City = address.Town ?? string.Empty, Country = address.CountryCode ?? string.Empty