You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
741 lines
32 KiB
741 lines
32 KiB
/* |
|
* Copyright 2024 Axel Waggershauser |
|
*/ |
|
// SPDX-License-Identifier: Apache-2.0 |
|
|
|
namespace ZXingCpp { |
|
|
|
using System; |
|
#if NETSTANDARD |
|
using System.Text; |
|
#endif |
|
using System.Runtime.InteropServices; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
|
|
using static Dll; |
|
|
|
internal class Dll |
|
{ |
|
private const string DllName = "ZXing"; |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_ReaderOptions_new(); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_delete(IntPtr opts); |
|
|
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setTryHarder(IntPtr opts, bool tryHarder); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getTryHarder(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setTryRotate(IntPtr opts, bool tryRotate); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getTryRotate(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setTryInvert(IntPtr opts, bool tryInvert); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getTryInvert(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setTryDownscale(IntPtr opts, bool tryDownscale); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getTryDownscale(IntPtr opts); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getIsPure(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setIsPure(IntPtr opts, bool isPure); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setValidateOptionalChecksum(IntPtr opts, bool validateOptionalChecksum); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getValidateOptionalChecksum(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setReturnErrors(IntPtr opts, bool returnErrors); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_ReaderOptions_getReturnErrors(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setFormats(IntPtr opts, BarcodeFormat[] formats, int count); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_ReaderOptions_getFormats(IntPtr opts, out int count); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setBinarizer(IntPtr opts, Binarizer binarizer); |
|
[DllImport(DllName)] public static extern Binarizer ZXing_ReaderOptions_getBinarizer(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setEanAddOnSymbol(IntPtr opts, EanAddOnSymbol eanAddOnSymbol); |
|
[DllImport(DllName)] public static extern EanAddOnSymbol ZXing_ReaderOptions_getEanAddOnSymbol(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setTextMode(IntPtr opts, TextMode textMode); |
|
[DllImport(DllName)] public static extern TextMode ZXing_ReaderOptions_getTextMode(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setMinLineCount(IntPtr opts, int n); |
|
[DllImport(DllName)] public static extern int ZXing_ReaderOptions_getMinLineCount(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_ReaderOptions_setMaxNumberOfSymbols(IntPtr opts, int n); |
|
[DllImport(DllName)] public static extern int ZXing_ReaderOptions_getMaxNumberOfSymbols(IntPtr opts); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_PositionToString(Position position); |
|
[DllImport(DllName)] public static extern BarcodeFormat ZXing_BarcodeFormatSymbology(BarcodeFormat format); |
|
[DllImport(DllName)] public static extern BarcodeFormat ZXing_BarcodeFormatFromString(string str); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_BarcodeFormatToString(BarcodeFormat format); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_BarcodeFormatsList(BarcodeFormat filter, out int count); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_BarcodeFormatsFromString(string str, out int count); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_BarcodeFormatsToString(BarcodeFormat[] format, int count); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_ImageView_new(IntPtr data, int width, int height, ImageFormat format, int rowStride, int pixStride); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_ImageView_new_checked(ref byte data, int size, int width, int height, ImageFormat format, int rowStride, int pixStride); |
|
[DllImport(DllName)] public static extern void ZXing_ImageView_delete(IntPtr iv); |
|
|
|
[DllImport(DllName)] public static extern void ZXing_Image_delete(IntPtr img); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Image_data(IntPtr img); |
|
[DllImport(DllName)] public static extern int ZXing_Image_width(IntPtr img); |
|
[DllImport(DllName)] public static extern int ZXing_Image_height(IntPtr img); |
|
[DllImport(DllName)] public static extern ImageFormat ZXing_Image_format(IntPtr img); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_ReadBarcodes(IntPtr iv, IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_Barcode_delete(IntPtr barcode); |
|
[DllImport(DllName)] public static extern void ZXing_Barcodes_delete(IntPtr barcodes); |
|
[DllImport(DllName)] public static extern int ZXing_Barcodes_size(IntPtr barcodes); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcodes_move(IntPtr barcodes, int i); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_CreatorOptions_new(BarcodeFormat format); |
|
[DllImport(DllName)] public static extern void ZXing_CreatorOptions_delete(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_CreatorOptions_setFormat(IntPtr opts, BarcodeFormat format); |
|
[DllImport(DllName)] public static extern BarcodeFormat ZXing_CreatorOptions_getFormat(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_CreatorOptions_setOptions(IntPtr opts, string options); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_CreatorOptions_getOptions(IntPtr opts); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_WriterOptions_new(); |
|
[DllImport(DllName)] public static extern void ZXing_WriterOptions_delete(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_WriterOptions_setScale(IntPtr opts, int scale); |
|
[DllImport(DllName)] public static extern int ZXing_WriterOptions_getScale(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_WriterOptions_setRotate(IntPtr opts, int rotate); |
|
[DllImport(DllName)] public static extern int ZXing_WriterOptions_getRotate(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_WriterOptions_setAddHRT(IntPtr opts, bool addHRT); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_WriterOptions_getAddHRT(IntPtr opts); |
|
[DllImport(DllName)] public static extern void ZXing_WriterOptions_setAddQuietZones(IntPtr opts, bool addQuietZones); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_WriterOptions_getAddQuietZones(IntPtr opts); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_CreateBarcodeFromText(string data, int size, IntPtr opts); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_CreateBarcodeFromBytes(byte[] data, int size, IntPtr opts); |
|
|
|
[DllImport(DllName)] public static extern IntPtr ZXing_WriteBarcodeToSVG(IntPtr barcode, IntPtr opts); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_WriteBarcodeToImage(IntPtr barcode, IntPtr opts); |
|
|
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_Barcode_isValid(IntPtr barcode); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_errorMsg(IntPtr barcode); |
|
[DllImport(DllName)] public static extern ErrorType ZXing_Barcode_errorType(IntPtr barcode); |
|
[DllImport(DllName)] public static extern BarcodeFormat ZXing_Barcode_format(IntPtr barcode); |
|
[DllImport(DllName)] public static extern BarcodeFormat ZXing_Barcode_symbology(IntPtr barcode); |
|
[DllImport(DllName)] public static extern ContentType ZXing_Barcode_contentType(IntPtr barcode); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_bytes(IntPtr barcode, out int len); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_bytesECI(IntPtr barcode, out int len); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_text(IntPtr barcode); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_symbologyIdentifier(IntPtr barcode); |
|
[DllImport(DllName)] public static extern Position ZXing_Barcode_position(IntPtr barcode); |
|
[DllImport(DllName)] public static extern int ZXing_Barcode_orientation(IntPtr barcode); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_Barcode_hasECI(IntPtr barcode); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_Barcode_isInverted(IntPtr barcode); |
|
[DllImport(DllName)] [return:MarshalAs(UnmanagedType.I1)] public static extern bool ZXing_Barcode_isMirrored(IntPtr barcode); |
|
[DllImport(DllName)] public static extern int ZXing_Barcode_lineCount(IntPtr barcode); |
|
[DllImport(DllName)] public static extern int ZXing_Barcode_sequenceIndex(IntPtr barcode); |
|
[DllImport(DllName)] public static extern int ZXing_Barcode_sequenceSize(IntPtr barcode); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_sequenceId(IntPtr barcode); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_Barcode_extra(IntPtr barcode, string? key); |
|
|
|
[DllImport(DllName)] public static extern void ZXing_free(IntPtr opts); |
|
[DllImport(DllName)] public static extern IntPtr ZXing_LastErrorMsg(); |
|
|
|
|
|
public static IntPtr CheckError(IntPtr ptr, string? msg = null) |
|
{ |
|
if (ptr == IntPtr.Zero) |
|
throw new Exception(msg ?? MarshalAsString(ZXing_LastErrorMsg())); |
|
return ptr; |
|
} |
|
|
|
public static string MarshalAsString(IntPtr ptr) |
|
{ |
|
ptr = CheckError(ptr, "ZXing C-API returned a NULL char*."); |
|
#if NET |
|
string res = Marshal.PtrToStringUTF8(ptr) ?? ""; |
|
#else |
|
string res; |
|
unsafe |
|
{ |
|
int length = 0; |
|
for (byte* i = (byte*)ptr; *i != 0; i++, length++); |
|
res = Encoding.UTF8.GetString((byte*)ptr, length); |
|
} |
|
#endif |
|
ZXing_free(ptr); |
|
return res; |
|
} |
|
|
|
public delegate IntPtr RetBytesFunc(IntPtr ptr, out int len); |
|
|
|
public static byte[] MarshalAsBytes(RetBytesFunc func, IntPtr d) |
|
{ |
|
IntPtr ptr = CheckError(func(d, out int len), "ZXing C-API returned a NULL byte*."); |
|
byte[] res = new byte[len]; |
|
Marshal.Copy(ptr, res, 0, len); |
|
ZXing_free(ptr); |
|
return res; |
|
} |
|
|
|
public static BarcodeFormat[] MarshalAsFormats(IntPtr ptr, int count) |
|
{ |
|
if (ptr == IntPtr.Zero || count <= 0) |
|
return Array.Empty<BarcodeFormat>(); |
|
|
|
int[] vals = new int[count]; |
|
Marshal.Copy(ptr, vals, 0, count); |
|
var res = Array.ConvertAll(vals, v => (BarcodeFormat)v); |
|
ZXing_free(ptr); |
|
return res; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Represents a barcode format/symbology. Can represent a specific format (e.g., QRCode) |
|
/// or a combination of formats (e.g., AllReadable). |
|
/// </summary> |
|
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] |
|
public readonly struct BarcodeFormat : IEquatable<BarcodeFormat> |
|
{ |
|
private readonly int _value; |
|
private BarcodeFormat(int v) => _value = v; |
|
|
|
public static readonly BarcodeFormat Invalid = new BarcodeFormat(0xFFFF); |
|
public static readonly BarcodeFormat None = new BarcodeFormat(0x0000); |
|
public static readonly BarcodeFormat All = new BarcodeFormat(0x2A2A); |
|
public static readonly BarcodeFormat AllReadable = new BarcodeFormat(0x722A); |
|
public static readonly BarcodeFormat AllCreatable = new BarcodeFormat(0x772A); |
|
public static readonly BarcodeFormat AllLinear = new BarcodeFormat(0x6C2A); |
|
public static readonly BarcodeFormat AllMatrix = new BarcodeFormat(0x6D2A); |
|
public static readonly BarcodeFormat AllGS1 = new BarcodeFormat(0x472A); |
|
public static readonly BarcodeFormat AllRetail = new BarcodeFormat(0x522A); |
|
public static readonly BarcodeFormat AllIndustrial = new BarcodeFormat(0x492A); |
|
public static readonly BarcodeFormat Codabar = new BarcodeFormat(0x2046); |
|
public static readonly BarcodeFormat Code39 = new BarcodeFormat(0x2041); |
|
public static readonly BarcodeFormat Code39Std = new BarcodeFormat(0x7341); |
|
public static readonly BarcodeFormat Code39Ext = new BarcodeFormat(0x6541); |
|
public static readonly BarcodeFormat Code32 = new BarcodeFormat(0x3241); |
|
public static readonly BarcodeFormat PZN = new BarcodeFormat(0x7041); |
|
public static readonly BarcodeFormat Code93 = new BarcodeFormat(0x2047); |
|
public static readonly BarcodeFormat Code128 = new BarcodeFormat(0x2043); |
|
public static readonly BarcodeFormat ITF = new BarcodeFormat(0x2049); |
|
public static readonly BarcodeFormat ITF14 = new BarcodeFormat(0x3449); |
|
public static readonly BarcodeFormat DataBar = new BarcodeFormat(0x2065); |
|
public static readonly BarcodeFormat DataBarOmni = new BarcodeFormat(0x6F65); |
|
public static readonly BarcodeFormat DataBarStk = new BarcodeFormat(0x7365); |
|
public static readonly BarcodeFormat DataBarStkOmni = new BarcodeFormat(0x4F65); |
|
public static readonly BarcodeFormat DataBarLtd = new BarcodeFormat(0x6C65); |
|
public static readonly BarcodeFormat DataBarExp = new BarcodeFormat(0x6565); |
|
public static readonly BarcodeFormat DataBarExpStk = new BarcodeFormat(0x4565); |
|
public static readonly BarcodeFormat EANUPC = new BarcodeFormat(0x2045); |
|
public static readonly BarcodeFormat EAN13 = new BarcodeFormat(0x3145); |
|
public static readonly BarcodeFormat EAN8 = new BarcodeFormat(0x3845); |
|
public static readonly BarcodeFormat EAN5 = new BarcodeFormat(0x3545); |
|
public static readonly BarcodeFormat EAN2 = new BarcodeFormat(0x3245); |
|
public static readonly BarcodeFormat ISBN = new BarcodeFormat(0x6945); |
|
public static readonly BarcodeFormat UPCA = new BarcodeFormat(0x6145); |
|
public static readonly BarcodeFormat UPCE = new BarcodeFormat(0x6545); |
|
public static readonly BarcodeFormat Telepen = new BarcodeFormat(0x2042); |
|
public static readonly BarcodeFormat TelepenAlpha = new BarcodeFormat(0x3042); |
|
public static readonly BarcodeFormat TelepenNumeric = new BarcodeFormat(0x3142); |
|
public static readonly BarcodeFormat OtherBarcode = new BarcodeFormat(0x2058); |
|
public static readonly BarcodeFormat DXFilmEdge = new BarcodeFormat(0x7858); |
|
public static readonly BarcodeFormat PDF417 = new BarcodeFormat(0x204C); |
|
public static readonly BarcodeFormat CompactPDF417 = new BarcodeFormat(0x634C); |
|
public static readonly BarcodeFormat MicroPDF417 = new BarcodeFormat(0x6D4C); |
|
public static readonly BarcodeFormat Aztec = new BarcodeFormat(0x207A); |
|
public static readonly BarcodeFormat AztecCode = new BarcodeFormat(0x637A); |
|
public static readonly BarcodeFormat AztecRune = new BarcodeFormat(0x727A); |
|
public static readonly BarcodeFormat QRCode = new BarcodeFormat(0x2051); |
|
public static readonly BarcodeFormat QRCodeModel1 = new BarcodeFormat(0x3151); |
|
public static readonly BarcodeFormat QRCodeModel2 = new BarcodeFormat(0x3251); |
|
public static readonly BarcodeFormat MicroQRCode = new BarcodeFormat(0x6D51); |
|
public static readonly BarcodeFormat RMQRCode = new BarcodeFormat(0x7251); |
|
public static readonly BarcodeFormat DataMatrix = new BarcodeFormat(0x2064); |
|
public static readonly BarcodeFormat MaxiCode = new BarcodeFormat(0x2055); |
|
|
|
public static implicit operator int(BarcodeFormat f) => f._value; |
|
public static implicit operator BarcodeFormat(int v) => new BarcodeFormat(v); |
|
|
|
public bool Equals(BarcodeFormat other) => _value == other._value; |
|
public override bool Equals(object? obj) => obj is BarcodeFormat other && Equals(other); |
|
public override int GetHashCode() => _value.GetHashCode(); |
|
public static bool operator ==(BarcodeFormat a, BarcodeFormat b) => a.Equals(b); |
|
public static bool operator !=(BarcodeFormat a, BarcodeFormat b) => !a.Equals(b); |
|
|
|
/// <summary>Converts the barcode format to its human readable string representation.</summary> |
|
/// <returns>The format name (e.g., "QR Code", "DataBar Limited").</returns> |
|
public override string ToString() => MarshalAsString(ZXing_BarcodeFormatToString(this)); |
|
|
|
/// <summary>Parses a string into a BarcodeFormat.</summary> |
|
/// <param name="s">Format name (case-insensitive).</param> |
|
/// <exception cref="Exception">Thrown when the string is not a valid format name.</exception> |
|
public static BarcodeFormat Parse(string s) |
|
{ |
|
if (!TryParse(s, out var fmt)) |
|
throw new Exception($"Invalid BarcodeFormat: '{s}'"); |
|
return fmt; |
|
} |
|
|
|
/// <summary>Attempts to parse a string into a BarcodeFormat.</summary> |
|
/// <returns>true if parsing succeeded; otherwise false.</returns> |
|
public static bool TryParse(string? s, out BarcodeFormat format) |
|
{ |
|
format = ZXing_BarcodeFormatFromString(s ?? ""); |
|
return format != Invalid; |
|
} |
|
|
|
/// <summary>Gets the symbology for this format (e.g., EAN13 returns EANUPC).</summary> |
|
public BarcodeFormat Symbology() => ZXing_BarcodeFormatSymbology(this); |
|
} |
|
|
|
/// <summary>Binarization algorithm for converting grayscale images to black/white for detection.</summary> |
|
public enum Binarizer |
|
{ |
|
/// <summary>Average of neighboring pixels for matrix, GlobalHistogram for linear (HybridBinarizer).</summary> |
|
LocalAverage, |
|
/// <summary>Valley between the 2 largest peaks in the histogram (per line in linear case).</summary> |
|
GlobalHistogram, |
|
/// <summary>Fixed threshold at 127.</summary> |
|
FixedThreshold, |
|
/// <summary>Threshold at 0, fastest option.</summary> |
|
BoolCast, |
|
}; |
|
|
|
/// <summary>Handling of EAN-2/EAN-5 Add-On symbols.</summary> |
|
public enum EanAddOnSymbol |
|
{ |
|
/// <summary>Ignore any Add-On symbol during read/scan.</summary> |
|
Ignore, |
|
/// <summary>Read EAN-2/EAN-5 Add-On symbol if found.</summary> |
|
Read, |
|
/// <summary>Require EAN-2/EAN-5 Add-On symbol to be present.</summary> |
|
Require, |
|
}; |
|
|
|
/// <summary>Text encoding mode that controls how barcode content bytes are converted to strings.</summary> |
|
public enum TextMode |
|
{ |
|
/// <summary>bytes() transcoded to unicode based on ECI info or guessed charset (default mode prior to 2.0).</summary> |
|
Plain, |
|
/// <summary>Standard content following the ECI protocol with every character set ECI segment transcoded to unicode.</summary> |
|
ECI, |
|
/// <summary>Human Readable Interpretation (dependent on the ContentType).</summary> |
|
HRI, |
|
/// <summary>Use EscapeNonGraphical() function (e.g., ASCII 29 becomes "<GS>").</summary> |
|
Escaped, |
|
/// <summary>bytes() transcoded to ASCII string of HEX values.</summary> |
|
Hex, |
|
/// <summary>bytesECI() transcoded to ASCII string of HEX values.</summary> |
|
HexECI, |
|
}; |
|
|
|
public enum ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; |
|
|
|
public enum ErrorType { None, Format, Checksum, Unsupported }; |
|
|
|
public enum ImageFormat { |
|
None = 0, |
|
Lum = 0x01000000, |
|
LumA = 0x02000000, |
|
RGB = 0x03000102, |
|
BGR = 0x03020100, |
|
RGBA = 0x04000102, |
|
ARGB = 0x04010203, |
|
BGRA = 0x04020100, |
|
ABGR = 0x04030201, |
|
}; |
|
|
|
public struct PointI |
|
{ |
|
public int X, Y; |
|
}; |
|
|
|
public struct Position |
|
{ |
|
public PointI TopLeft, TopRight, BottomRight, BottomLeft; |
|
|
|
public override string ToString() => MarshalAsString(ZXing_PositionToString(this)); |
|
}; |
|
|
|
/// <summary> |
|
/// Non-owning view into image data for barcode detection. Does not copy pixel data. |
|
/// </summary> |
|
public class ImageView |
|
{ |
|
internal IntPtr _d; |
|
|
|
#if NET |
|
public ImageView(ReadOnlySpan<byte> data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0) |
|
=> _d = CheckError(ZXing_ImageView_new_checked(ref MemoryMarshal.GetReference(data), data.Length, width, height, format, rowStride, pixStride)); |
|
#else |
|
public ImageView(byte[] data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0) |
|
=> _d = CheckError(ZXing_ImageView_new_checked(ref data[0], data.Length, width, height, format, rowStride, pixStride)); |
|
#endif |
|
|
|
public ImageView(IntPtr data, int width, int height, ImageFormat format, int rowStride = 0, int pixStride = 0) |
|
=> _d = CheckError(ZXing_ImageView_new(data, width, height, format, rowStride, pixStride)); |
|
|
|
~ImageView() => ZXing_ImageView_delete(_d); |
|
} |
|
|
|
/// <summary> |
|
/// Owns barcode image data. Returned by Barcode.ToImage(). |
|
/// </summary> |
|
public class Image : IDisposable |
|
{ |
|
internal IntPtr _d; |
|
|
|
internal Image(IntPtr d) => _d = d; |
|
|
|
~Image() => Dispose(); |
|
|
|
public void Dispose() |
|
{ |
|
ZXing_Image_delete(_d); |
|
_d = IntPtr.Zero; |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
public IntPtr Data => ZXing_Image_data(_d); |
|
public int Width => ZXing_Image_width(_d); |
|
public int Height => ZXing_Image_height(_d); |
|
public ImageFormat Format => ZXing_Image_format(_d); |
|
|
|
public byte[] ToArray() |
|
{ |
|
IntPtr ptr = ZXing_Image_data(_d); |
|
if (ptr == IntPtr.Zero) |
|
return new byte[0]; |
|
|
|
int len = Width * Height; |
|
byte[] res = new byte[len]; |
|
Marshal.Copy(ptr, res, 0, len); |
|
return res; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Collection of barcode formats, supporting combinations like "QRCode, DataMatrix". |
|
/// </summary> |
|
public class BarcodeFormats : IReadOnlyCollection<BarcodeFormat> |
|
{ |
|
private readonly BarcodeFormat[] _d; |
|
|
|
public BarcodeFormats() => _d = Array.Empty<BarcodeFormat>(); |
|
public BarcodeFormats(params BarcodeFormat[] formats) => _d = formats ?? Array.Empty<BarcodeFormat>(); |
|
public BarcodeFormats(IEnumerable<BarcodeFormat> formats) => _d = formats?.ToArray() ?? Array.Empty<BarcodeFormat>(); |
|
|
|
public static implicit operator BarcodeFormats(BarcodeFormat format) => new BarcodeFormats(format); |
|
public static implicit operator BarcodeFormat[](BarcodeFormats formats) => formats._d; |
|
|
|
public bool Contains(BarcodeFormat format) => Array.IndexOf(_d, format) >= 0; |
|
public int Count => _d.Length; |
|
public bool IsEmpty => _d.Length == 0; |
|
|
|
public IEnumerator<BarcodeFormat> GetEnumerator() => ((IEnumerable<BarcodeFormat>)_d).GetEnumerator(); |
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => _d.GetEnumerator(); |
|
|
|
/// <summary>Lists all supported barcode formats, optionally filtered by a specific format.</summary> |
|
/// <param name="filter">e.g. AllReadable or AllLinear.</param> |
|
/// <returns>All supported barcode formats that match the filter.</returns> |
|
public static BarcodeFormats List(BarcodeFormat filter) |
|
{ |
|
IntPtr ptr = ZXing_BarcodeFormatsList(filter, out int count); |
|
return new BarcodeFormats(MarshalAsFormats(ptr, count)); |
|
} |
|
|
|
public static bool TryParse(string str, out BarcodeFormats formats) |
|
{ |
|
IntPtr ptr = ZXing_BarcodeFormatsFromString(str, out int count); |
|
formats = new BarcodeFormats(MarshalAsFormats(ptr, count)); |
|
return formats.Count > 0; |
|
} |
|
|
|
public static BarcodeFormats Parse(string str) |
|
{ |
|
if (TryParse(str, out var formats)) |
|
return formats; |
|
|
|
throw new Exception($"'{str}' is not a valid set of barcode formats."); |
|
} |
|
|
|
public override string ToString() => MarshalAsString(ZXing_BarcodeFormatsToString(_d, _d.Length)); |
|
} |
|
|
|
/// <summary>Configuration options for barcode reading/detection.</summary> |
|
public class ReaderOptions : IDisposable |
|
{ |
|
internal IntPtr _d; |
|
|
|
public ReaderOptions() => _d = CheckError(ZXing_ReaderOptions_new(), "Failed to create ReaderOptions."); |
|
|
|
~ReaderOptions() => Dispose(); |
|
|
|
public void Dispose() |
|
{ |
|
ZXing_ReaderOptions_delete(_d); |
|
_d = IntPtr.Zero; |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
/// <summary>Spend more time to find barcodes; slower but more accurate.</summary> |
|
public bool TryHarder |
|
{ |
|
get => ZXing_ReaderOptions_getTryHarder(_d); |
|
set => ZXing_ReaderOptions_setTryHarder(_d, value); |
|
} |
|
|
|
/// <summary>Also detect barcodes in 90/180/270 degree rotated images.</summary> |
|
public bool TryRotate |
|
{ |
|
get => ZXing_ReaderOptions_getTryRotate(_d); |
|
set => ZXing_ReaderOptions_setTryRotate(_d, value); |
|
} |
|
|
|
/// <summary>Also try detecting inverted (white on black) barcodes.</summary> |
|
public bool TryInvert |
|
{ |
|
get => ZXing_ReaderOptions_getTryInvert(_d); |
|
set => ZXing_ReaderOptions_setTryInvert(_d, value); |
|
} |
|
|
|
/// <summary>Try downscaled images (high resolution images can hamper the detection).</summary> |
|
public bool TryDownscale |
|
{ |
|
get => ZXing_ReaderOptions_getTryDownscale(_d); |
|
set => ZXing_ReaderOptions_setTryDownscale(_d, value); |
|
} |
|
|
|
/// <summary>Assume the image contains only a single, perfectly aligned barcode, nothing else.</summary> |
|
public bool IsPure |
|
{ |
|
get => ZXing_ReaderOptions_getIsPure(_d); |
|
set => ZXing_ReaderOptions_setIsPure(_d, value); |
|
} |
|
|
|
/// <summary>Validate optional checksums (e.g., Code39, ITF).</summary> |
|
public bool ValidateOptionalChecksum |
|
{ |
|
get => ZXing_ReaderOptions_getValidateOptionalChecksum(_d); |
|
set => ZXing_ReaderOptions_setValidateOptionalChecksum(_d, value); |
|
} |
|
|
|
/// <summary>Return invalid barcodes with error information instead of skipping them.</summary> |
|
public bool ReturnErrors |
|
{ |
|
get => ZXing_ReaderOptions_getReturnErrors(_d); |
|
set => ZXing_ReaderOptions_setReturnErrors(_d, value); |
|
} |
|
|
|
/// <summary>Barcode formats to search for (increase performance by limiting the set).</summary> |
|
public BarcodeFormats Formats |
|
{ |
|
get { |
|
IntPtr ptr = ZXing_ReaderOptions_getFormats(_d, out int count); |
|
return new BarcodeFormats(MarshalAsFormats(ptr, count)); |
|
} |
|
set { |
|
ZXing_ReaderOptions_setFormats(_d, value, value.Count); |
|
} |
|
} |
|
|
|
public Binarizer Binarizer |
|
{ |
|
get => ZXing_ReaderOptions_getBinarizer(_d); |
|
set => ZXing_ReaderOptions_setBinarizer(_d, value); |
|
} |
|
|
|
public EanAddOnSymbol EanAddOnSymbol |
|
{ |
|
get => ZXing_ReaderOptions_getEanAddOnSymbol(_d); |
|
set => ZXing_ReaderOptions_setEanAddOnSymbol(_d, value); |
|
} |
|
|
|
public TextMode TextMode |
|
{ |
|
get => ZXing_ReaderOptions_getTextMode(_d); |
|
set => ZXing_ReaderOptions_setTextMode(_d, value); |
|
} |
|
|
|
/// <summary>Minimum number of lines for linear barcodes (default is 2).</summary> |
|
public int MinLineCount |
|
{ |
|
get => ZXing_ReaderOptions_getMinLineCount(_d); |
|
set => ZXing_ReaderOptions_setMinLineCount(_d, value); |
|
} |
|
|
|
/// <summary>Maximum number of symbols to detect (can be used to limit processing time).</summary> |
|
public int MaxNumberOfSymbols |
|
{ |
|
get => ZXing_ReaderOptions_getMaxNumberOfSymbols(_d); |
|
set => ZXing_ReaderOptions_setMaxNumberOfSymbols(_d, value); |
|
} |
|
|
|
} |
|
|
|
/// <summary>Options for creating/encoding barcodes.</summary> |
|
public class CreatorOptions : IDisposable |
|
{ |
|
internal IntPtr _d; |
|
|
|
public CreatorOptions(BarcodeFormat format, string? options = null) |
|
{ |
|
_d = CheckError(ZXing_CreatorOptions_new(format), "Failed to create CreatorOptions."); |
|
if (options != null) |
|
Options = options; |
|
} |
|
|
|
public static implicit operator CreatorOptions(BarcodeFormat f) => new CreatorOptions(f); |
|
|
|
~CreatorOptions() => Dispose(); |
|
|
|
public void Dispose() |
|
{ |
|
ZXing_CreatorOptions_delete(_d); |
|
_d = IntPtr.Zero; |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
public BarcodeFormat Format |
|
{ |
|
get => ZXing_CreatorOptions_getFormat(_d); |
|
set => ZXing_CreatorOptions_setFormat(_d, value); |
|
} |
|
|
|
/// <summary>Format-specific options as key=value pairs (e.g., "EcLevel=H").</summary> |
|
/// <remarks>This can be a serialized JSON object or simple key=value pairs separated by commas.</remarks> |
|
public String Options |
|
{ |
|
get => MarshalAsString(ZXing_CreatorOptions_getOptions(_d)); |
|
set => ZXing_CreatorOptions_setOptions(_d, value); |
|
} |
|
|
|
} |
|
|
|
/// <summary>Options for rendering barcodes to images or SVG.</summary> |
|
public class WriterOptions : IDisposable |
|
{ |
|
internal IntPtr _d; |
|
|
|
public WriterOptions() => _d = CheckError(ZXing_WriterOptions_new(), "Failed to create WriterOptions."); |
|
|
|
~WriterOptions() => Dispose(); |
|
|
|
public void Dispose() |
|
{ |
|
ZXing_WriterOptions_delete(_d); |
|
_d = IntPtr.Zero; |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
/// <summary>Scaling factor for the barcode modules (>0 means 'pixels per module', <0 means 'target size in pixels').</summary> |
|
public int Scale |
|
{ |
|
get => ZXing_WriterOptions_getScale(_d); |
|
set => ZXing_WriterOptions_setScale(_d, value); |
|
} |
|
|
|
/// <summary>Rotation in degrees (0, 90, 180, or 270).</summary> |
|
public int Rotate |
|
{ |
|
get => ZXing_WriterOptions_getRotate(_d); |
|
set => ZXing_WriterOptions_setRotate(_d, value); |
|
} |
|
|
|
/// <summary>Add Human Readable Text (the barcode content) below linear barcodes.</summary> |
|
public bool AddHRT |
|
{ |
|
get => ZXing_WriterOptions_getAddHRT(_d); |
|
set => ZXing_WriterOptions_setAddHRT(_d, value); |
|
} |
|
|
|
/// <summary>Add standard quiet zones (white margins) around the barcode.</summary> |
|
public bool AddQuietZones |
|
{ |
|
get => ZXing_WriterOptions_getAddQuietZones(_d); |
|
set => ZXing_WriterOptions_setAddQuietZones(_d, value); |
|
} |
|
} |
|
|
|
/// <summary>A detected or created barcode with its data, format, and metadata.</summary> |
|
public class Barcode : IDisposable |
|
{ |
|
internal IntPtr _d; |
|
|
|
internal Barcode(IntPtr d) => _d = d; |
|
|
|
~Barcode() => Dispose(); |
|
|
|
public void Dispose() |
|
{ |
|
ZXing_Barcode_delete(_d); |
|
_d = IntPtr.Zero; |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
public Barcode(string data, CreatorOptions opts) |
|
=> _d = CheckError(ZXing_CreateBarcodeFromText(data, data.Length, opts._d)); |
|
|
|
public Barcode(byte[] data, CreatorOptions opts) |
|
=> _d = CheckError(ZXing_CreateBarcodeFromBytes(data, data.Length, opts._d)); |
|
|
|
/// <summary>True if successfully decoded or created without errors.</summary> |
|
public bool IsValid => ZXing_Barcode_isValid(_d); |
|
public BarcodeFormat Format => ZXing_Barcode_format(_d); |
|
/// <summary>Base symbology (e.g., EAN13 -> EANUPC).</summary> |
|
public BarcodeFormat Symbology => ZXing_Barcode_symbology(_d); |
|
public ContentType ContentType => ZXing_Barcode_contentType(_d); |
|
public string Text => MarshalAsString(ZXing_Barcode_text(_d)); |
|
public byte[] Bytes => MarshalAsBytes(ZXing_Barcode_bytes, _d); |
|
/// <summary>Bytes with Extended Channel Interpretation markers included.</summary> |
|
public byte[] BytesECI => MarshalAsBytes(ZXing_Barcode_bytesECI, _d); |
|
/// <summary>ISO/IEC 15424 symbology identifier (e.g., "]Q1" for QR Code).</summary> |
|
public string SymbologyIdentifier => MarshalAsString(ZXing_Barcode_symbologyIdentifier(_d)); |
|
public string ErrorMsg => MarshalAsString(ZXing_Barcode_errorMsg(_d)); |
|
public ErrorType ErrorType => ZXing_Barcode_errorType(_d); |
|
/// <summary>Corner points of the barcode in the image.</summary> |
|
public Position Position => ZXing_Barcode_position(_d); |
|
/// <summary>Detected rotation in degrees.</summary> |
|
public int Orientation => ZXing_Barcode_orientation(_d); |
|
public bool HasECI => ZXing_Barcode_hasECI(_d); |
|
public bool IsInverted => ZXing_Barcode_isInverted(_d); |
|
public bool IsMirrored => ZXing_Barcode_isMirrored(_d); |
|
public int LineCount => ZXing_Barcode_lineCount(_d); |
|
public int SequenceIndex => ZXing_Barcode_sequenceIndex(_d); |
|
public int SequenceSize => ZXing_Barcode_sequenceSize(_d); |
|
public string SequenceId => MarshalAsString(ZXing_Barcode_sequenceId(_d)); |
|
/// <summary> |
|
/// Additional format-specific metadata as JSON pairs (e.g., '{"EcLevel":"H","Version":5}' for QR Codes). |
|
/// </summary> |
|
/// <param name="key"></param> |
|
/// <returns>JSON string or value of the specified key in the extra metadata.</returns> |
|
public string Extra(string? key = null) => MarshalAsString(ZXing_Barcode_extra(_d, key)); |
|
|
|
public string ToSVG(WriterOptions? opts = null) |
|
=> MarshalAsString(CheckError(ZXing_WriteBarcodeToSVG(_d, opts?._d ?? IntPtr.Zero))); |
|
|
|
public Image ToImage(WriterOptions? opts = null) |
|
=> new Image(CheckError(ZXing_WriteBarcodeToImage(_d, opts?._d ?? IntPtr.Zero))); |
|
} |
|
|
|
/// <summary>Barcode reader. Inherits ReaderOptions for convenient configuration.</summary> |
|
public class BarcodeReader : ReaderOptions |
|
{ |
|
/// <summary>Scans an image for barcodes.</summary> |
|
/// <param name="iv">Image to scan.</param> |
|
/// <param name="opts">Optional configuration; uses defaults if null.</param> |
|
/// <returns>Array of detected barcodes (empty if none found).</returns> |
|
public static Barcode[] Read(ImageView iv, ReaderOptions? opts = null) |
|
{ |
|
var ptr = CheckError(ZXing_ReadBarcodes(iv._d, opts?._d ?? IntPtr.Zero)); |
|
|
|
// return static empty array if no Barcodes are found, avoiding any managed heap allocation |
|
var res = Array.Empty<Barcode>(); |
|
var size = ZXing_Barcodes_size(ptr); |
|
if (size > 0) { |
|
res = new Barcode[size]; |
|
for (int i = 0; i < size; ++i) |
|
res[i] = new Barcode(ZXing_Barcodes_move(ptr, i)); |
|
} |
|
|
|
ZXing_Barcodes_delete(ptr); |
|
|
|
return res; |
|
} |
|
|
|
public Barcode[] From(ImageView iv) => Read(iv, this); |
|
} |
|
|
|
/// <summary>Barcode creator. Inherits CreatorOptions for convenient configuration.</summary> |
|
public class BarcodeCreator : CreatorOptions |
|
{ |
|
public BarcodeCreator(BarcodeFormat format) : base(format) {} |
|
|
|
public Barcode From(string data) => new Barcode(data, this); |
|
public Barcode From(byte[] data) => new Barcode(data, this); |
|
} |
|
|
|
}
|
|
|