diff --git a/NAPS2.Sdk/Scan/BarcodeDetectionOptions.cs b/NAPS2.Sdk/Scan/BarcodeDetectionOptions.cs
index 986e8aef49..a357ca7362 100644
--- a/NAPS2.Sdk/Scan/BarcodeDetectionOptions.cs
+++ b/NAPS2.Sdk/Scan/BarcodeDetectionOptions.cs
@@ -1,5 +1,3 @@
-using ZXing.Common;
-
namespace NAPS2.Scan;
///
@@ -10,6 +8,186 @@ public class BarcodeDetectionOptions
public bool DetectBarcodes { get; set; }
public bool PatchTOnly { get; set; }
-
- public DecodingOptions? ZXingOptions { get; set; }
+
+ ///
+ /// Image is a pure monochrome image of a barcode.
+ ///
+ ///
+ /// true if monochrome image of a barcode; otherwise, false.
+ ///
+ public bool PureBarcode { get; set; }
+
+ ///
+ /// Specifies what character encoding to use when decoding, where applicable (type String)
+ ///
+ ///
+ /// The character set.
+ ///
+ public string? CharacterSet { get; set; }
+
+ ///
+ /// Image is known to be of one of a few possible formats.
+ /// Bitfield enum of {@link BarcodeFormat}s.
+ ///
+ ///
+ /// The possible formats.
+ ///
+ public BarcodeFormat PossibleFormats { get; set; }
+
+ ///
+ /// if Code39 could be detected try to use extended mode for full ASCII character set
+ ///
+ public bool UseCode39ExtendedMode { get; set; }
+
+ ///
+ /// Don't fail if a Code39 is detected but can't be decoded in extended mode.
+ /// Return the raw Code39 result instead. Maps to .
+ ///
+ public bool UseCode39RelaxedExtendedMode { get; set; }
+
+ ///
+ /// Assume Code 39 codes employ a check digit. Maps to .
+ ///
+ ///
+ /// true if it should assume a Code 39 check digit; otherwise, false.
+ ///
+ public bool AssumeCode39CheckDigit { get; set; }
+
+ ///
+ /// If true, return the start and end digits in a Codabar barcode instead of stripping them. They
+ /// are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
+ /// to not be. Doesn't matter what it maps to; use .
+ ///
+ public bool ReturnCodabarStartEnd { get; set; }
+
+ ///
+ /// Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
+ /// For example this affects FNC1 handling for Code 128 (aka GS1-128).
+ ///
+ ///
+ /// true if it should assume GS1; otherwise, false.
+ ///
+ public bool AssumeGS1 { get; set; }
+
+ ///
+ /// Assume MSI codes employ a check digit. Maps to .
+ ///
+ ///
+ /// true if it should assume a MSI check digit; otherwise, false.
+ ///
+ public bool AssumeMSICheckDigit { get; set; }
+
+ ///
+ /// Allowed lengths of encoded data -- reject anything else. Maps to an int[].
+ ///
+ public int[]? AllowedLengths { get; set; }
+
+ ///
+ /// Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
+ /// Maps to an int[] of the allowed extension lengths, for example [2], [5], or [2, 5].
+ /// If it is optional to have an extension, do not set this hint. If this is set,
+ /// and a UPC or EAN barcode is found but an extension is not, then no result will be returned
+ /// at all.
+ ///
+ public int[]? AllowedEANExtensions { get; set; }
+
+}
+
+//
+// Summary:
+// Enumerates barcode formats known to this package.
+//
+[Flags]
+public enum BarcodeFormat
+{
+ //
+ // Summary:
+ // Aztec 2D barcode format.
+ AZTEC = 1,
+ //
+ // Summary:
+ // CODABAR 1D format.
+ CODABAR = 2,
+ //
+ // Summary:
+ // Code 39 1D format.
+ CODE_39 = 4,
+ //
+ // Summary:
+ // Code 93 1D format.
+ CODE_93 = 8,
+ //
+ // Summary:
+ // Code 128 1D format.
+ CODE_128 = 0x10,
+ //
+ // Summary:
+ // Data Matrix 2D barcode format.
+ DATA_MATRIX = 0x20,
+ //
+ // Summary:
+ // EAN-8 1D format.
+ EAN_8 = 0x40,
+ //
+ // Summary:
+ // EAN-13 1D format.
+ EAN_13 = 0x80,
+ //
+ // Summary:
+ // ITF (Interleaved Two of Five) 1D format.
+ ITF = 0x100,
+ //
+ // Summary:
+ // MaxiCode 2D barcode format.
+ MAXICODE = 0x200,
+ //
+ // Summary:
+ // PDF417 format.
+ PDF_417 = 0x400,
+ //
+ // Summary:
+ // QR Code 2D barcode format.
+ QR_CODE = 0x800,
+ //
+ // Summary:
+ // RSS 14
+ RSS_14 = 0x1000,
+ //
+ // Summary:
+ // RSS EXPANDED
+ RSS_EXPANDED = 0x2000,
+ //
+ // Summary:
+ // UPC-A 1D format.
+ UPC_A = 0x4000,
+ //
+ // Summary:
+ // UPC-E 1D format.
+ UPC_E = 0x8000,
+ //
+ // Summary:
+ // UPC/EAN extension format. Not a stand-alone format.
+ UPC_EAN_EXTENSION = 0x10000,
+ //
+ // Summary:
+ // MSI
+ MSI = 0x20000,
+ //
+ // Summary:
+ // Plessey
+ PLESSEY = 0x40000,
+ //
+ // Summary:
+ // Intelligent Mail barcode
+ IMB = 0x80000,
+ //
+ // Summary:
+ // Pharmacode format.
+ PHARMA_CODE = 0x100000,
+ //
+ // Summary:
+ // UPC_A | UPC_E | EAN_13 | EAN_8 | CODABAR | CODE_39 | CODE_93 | CODE_128 | ITF
+ // | RSS_14 | RSS_EXPANDED without MSI (to many false-positives) and IMB (not enough
+ // tested, and it looks more like a 2D)
+ All_1D = 0xF1DE
}
\ No newline at end of file
diff --git a/NAPS2.Sdk/Scan/BarcodeDetector.cs b/NAPS2.Sdk/Scan/BarcodeDetector.cs
index 8ee54e0c97..e040843161 100644
--- a/NAPS2.Sdk/Scan/BarcodeDetector.cs
+++ b/NAPS2.Sdk/Scan/BarcodeDetector.cs
@@ -10,7 +10,7 @@ namespace NAPS2.Scan;
///
internal static class BarcodeDetector
{
- private static readonly BarcodeFormat PATCH_T_FORMAT = BarcodeFormat.CODE_39;
+ private static readonly ZXing.BarcodeFormat PATCH_T_FORMAT = ZXing.BarcodeFormat.CODE_39;
public static Barcode Detect(IMemoryImage image, BarcodeDetectionOptions options)
{
@@ -20,11 +20,39 @@ public static Barcode Detect(IMemoryImage image, BarcodeDetectionOptions options
return Barcode.NoDetection;
}
- var zxingOptions = options.ZXingOptions ?? new DecodingOptions
+ // Create a ZXing DecodingOptions object based on the provided options.
+ var zxingOptions = new DecodingOptions
{
TryHarder = true,
- PossibleFormats = options.PatchTOnly ? [PATCH_T_FORMAT] : null
+ PureBarcode = options.PureBarcode,
+ CharacterSet = options.CharacterSet,
+ UseCode39ExtendedMode = options.UseCode39ExtendedMode,
+ UseCode39RelaxedExtendedMode = options.UseCode39RelaxedExtendedMode,
+ AssumeCode39CheckDigit = options.AssumeCode39CheckDigit,
+ ReturnCodabarStartEnd = options.ReturnCodabarStartEnd,
+ AssumeGS1 = options.AssumeGS1,
+ AssumeMSICheckDigit = options.AssumeMSICheckDigit,
+ AllowedLengths = options.AllowedLengths,
+ AllowedEANExtensions = options.AllowedEANExtensions
};
+ if (options.PatchTOnly)
+ {
+ zxingOptions.PossibleFormats ??= [];
+ zxingOptions.PossibleFormats = [PATCH_T_FORMAT];
+ }
+ else
+ {
+ // map the PossibleFormats bitfield to the matching ZXing.BarcodeFormat's
+ foreach (ZXing.BarcodeFormat format in Enum.GetValues(typeof(ZXing.BarcodeFormat)))
+ {
+ if ((((int) options.PossibleFormats) & (int) format) > 0)
+ {
+ zxingOptions.PossibleFormats ??= [];
+ zxingOptions.PossibleFormats.Add(format);
+ }
+ }
+ }
+
var reader = new BarcodeReader(x => new MemoryImageLuminanceSource(x))
{
Options = zxingOptions
diff --git a/NAPS2.Sdk/Scan/Internal/RemotePostProcessor.cs b/NAPS2.Sdk/Scan/Internal/RemotePostProcessor.cs
index 84ff388efa..38ef786a2d 100644
--- a/NAPS2.Sdk/Scan/Internal/RemotePostProcessor.cs
+++ b/NAPS2.Sdk/Scan/Internal/RemotePostProcessor.cs
@@ -174,10 +174,26 @@ options.Driver is not (Driver.Wia or Driver.Twain))
if (!data.Barcode.IsDetected)
{
// Even if barcode detection was attempted previously and failed, image adjustments may improve detection.
- data = data with
+ try
{
- Barcode = BarcodeDetector.Detect(image, options.BarcodeDetectionOptions)
- };
+ data = data with
+ {
+ Barcode = DetectBarcode(image, options.BarcodeDetectionOptions)
+ };
+ }
+ catch (FileNotFoundException ex)
+ {
+ if (data.Barcode == Barcode.NoDetection)
+ {
+ // If we don't want a barcode we can ignore this
+ _logger.LogError(ex, "ZXing library not found. But Barcode detection is not enabled anyway.");
+ }
+ else
+ {
+ // If we want a barcode we should throw
+ throw;
+ }
+ }
}
if (options.ThumbnailSize.HasValue)
{
@@ -193,6 +209,12 @@ options.Driver is not (Driver.Wia or Driver.Twain))
processedImage = processedImage.WithPostProcessingData(data, true);
}
+ // Do the barcode detection
+ private static Barcode DetectBarcode(IMemoryImage image, BarcodeDetectionOptions options)
+ {
+ return BarcodeDetector.Detect(image, options);
+ }
+
private string? SaveForBackgroundOcr(IMemoryImage bitmap, ScanOptions options)
{
if (!string.IsNullOrEmpty(options.OcrParams.LanguageCode))