Skip to main content

zxingcpp/
lib.rs

1/*
2* Copyright 2024 Axel Waggershauser
3*/
4// SPDX-License-Identifier: Apache-2.0
5
6#![allow(unknown_lints)] // backward compatibility
7#![allow(unused_unsafe)]
8#![allow(clippy::useless_transmute)]
9#![allow(clippy::redundant_closure_call)]
10#![allow(clippy::missing_transmute_annotations)] // introduced in 1.79
11
12mod tests;
13
14#[allow(dead_code)]
15#[allow(non_camel_case_types)]
16#[allow(non_snake_case)]
17#[allow(non_upper_case_globals)]
18mod bindings {
19	include!("bindings.rs");
20}
21
22use bindings::*;
23
24use paste::paste;
25use std::ffi::{c_char, c_int, c_void, CStr, CString, NulError};
26use std::fmt::{Display, Formatter};
27use std::marker::PhantomData;
28use std::mem::transmute;
29use std::ptr::null;
30use std::rc::Rc;
31use std::slice;
32use thiserror::Error;
33
34#[derive(Error, Debug)]
35pub enum Error {
36	#[error("{0}")]
37	InvalidInput(String),
38
39	#[error("NulError from CString::new")]
40	NulError(#[from] NulError),
41	//
42	// #[error("data store disconnected")]
43	// IOError(#[from] std::io::Error),
44	// #[error("the data for key `{0}` is not available")]
45	// Redaction(String),
46	// #[error("invalid header (expected {expected:?}, found {found:?})")]
47	// InvalidHeader {
48	//     expected: String,
49	//     found: String,
50	// },
51	// #[error("unknown data store error")]
52	// Unknown,
53}
54
55// see https://github.com/dtolnay/thiserror/issues/62
56impl From<std::convert::Infallible> for Error {
57	fn from(_: std::convert::Infallible) -> Self {
58		unreachable!()
59	}
60}
61
62fn c2r_str(str: *mut c_char) -> String {
63	let mut res = String::new();
64	if !str.is_null() {
65		unsafe { res = CStr::from_ptr(str).to_string_lossy().to_string() };
66		unsafe { ZXing_free(str as *mut c_void) };
67	}
68	res
69}
70
71fn c2r_vec(buf: *mut u8, len: c_int) -> Vec<u8> {
72	let mut res = Vec::<u8>::new();
73	if !buf.is_null() && len > 0 {
74		unsafe { res = std::slice::from_raw_parts(buf, len as usize).to_vec() };
75		unsafe { ZXing_free(buf as *mut c_void) };
76	}
77	res
78}
79
80fn last_error() -> Error {
81	match unsafe { ZXing_LastErrorMsg().as_mut() } {
82		None => panic!("Internal error: ZXing_LastErrorMsg() returned NULL"),
83		Some(error) => Error::InvalidInput(c2r_str(error)),
84	}
85}
86
87// MARK: - Convenience macros
88
89macro_rules! last_error_or {
90	($expr:expr) => {
91		match unsafe { ZXing_LastErrorMsg().as_mut() } {
92			None => Ok($expr),
93			Some(error) => Err(Error::InvalidInput(c2r_str(error))),
94		}
95	};
96}
97
98macro_rules! last_error_if_null_or {
99	($ptr:ident, $expr:expr) => {
100		match $ptr.is_null() {
101			true => Err(last_error()),
102			false => Ok($expr),
103		}
104	};
105}
106
107macro_rules! make_zxing_class {
108	($r_class:ident, $c_class:ident) => {
109		paste! {
110			pub struct $r_class(*mut $c_class);
111
112			impl Drop for $r_class {
113				fn drop(&mut self) {
114					unsafe { [<$c_class _delete>](self.0) }
115				}
116			}
117		}
118	};
119}
120
121macro_rules! make_zxing_class_with_default {
122	($r_class:ident, $c_class:ident) => {
123		make_zxing_class!($r_class, $c_class);
124		paste! {
125			impl $r_class {
126				pub fn new() -> Self {
127					unsafe { $r_class([<$c_class _new>]()) }
128				}
129			}
130
131			impl Default for $r_class {
132				fn default() -> Self {
133					Self::new()
134				}
135			}
136		}
137	};
138}
139
140macro_rules! getter {
141	($class:ident, $c_name:ident, $r_name:ident, $conv:expr, $type:ty) => {
142		pub fn $r_name(&self) -> $type {
143			paste! { unsafe { $conv([<ZXing_ $class _ $c_name>](self.0)) } }
144		}
145	};
146	($class:ident, $c_name:ident, $conv:expr, $type:ty) => {
147		paste! { getter! { $class, $c_name, [<$c_name:snake>], $conv, $type } }
148	};
149}
150
151/// Expands to a set of convenience accessors for a given wrapper type.
152///
153/// - A builder-style setter: `fn name(self, v: impl AsRef/Into) -> Self`
154/// - A mutable setter: `fn set_name(&mut self, v: impl AsRef/Into) -> &mut Self`
155/// - A getter: `fn get_name(&self) -> T`
156///
157/// Works for `String` and other value types. TODO: support slices/arrays
158macro_rules! property {
159	($class:ident, $c_name:ident, $r_name:ident, String) => {
160		pub fn $r_name(self, v: impl AsRef<str>) -> Self {
161			let cstr = CString::new(v.as_ref()).unwrap();
162			paste! { unsafe { [<ZXing_ $class _set $c_name>](self.0, cstr.as_ptr()) } };
163			self
164		}
165
166		paste! {
167			pub fn [<set_ $r_name>](&mut self, v : impl AsRef<str>) -> &mut Self {
168				let cstr = CString::new(v.as_ref()).unwrap();
169				unsafe { [<ZXing_ $class _set $c_name>](self.0, cstr.as_ptr()) };
170				self
171			}
172
173			pub fn [<get_ $r_name>](&self) -> String {
174				unsafe { c2r_str([<ZXing_ $class _get $c_name>](self.0)) }
175			}
176		}
177	};
178
179	($class:ident, $c_name:ident, $r_name:ident, $type:ty) => {
180		pub fn $r_name(self, v: impl Into<$type>) -> Self {
181			paste! { unsafe { [<ZXing_ $class _set $c_name>](self.0, transmute(v.into())) } };
182			self
183		}
184
185		paste! {
186			pub fn [<set_ $r_name>](&mut self, v : impl Into<$type>) -> &mut Self {
187				unsafe { [<ZXing_ $class _set $c_name>](self.0, transmute(v.into())) };
188				self
189			}
190
191			pub fn [<get_ $r_name>](&self) -> $type {
192				unsafe { transmute([<ZXing_ $class _get $c_name>](self.0)) }
193			}
194		}
195	};
196
197	($class:ident, $c_name:ident, $type:ty) => {
198		paste! { property! { $class, $c_name, [<$c_name:snake>], $type } }
199	};
200}
201
202macro_rules! make_zxing_enum {
203	($name:ident { $($field:ident),* }) => {
204		#[repr(u32)]
205		#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord)]
206		pub enum $name {
207			$($field = paste! { [<ZXing_ $name _ $field>] },)*
208		}
209	}
210}
211
212// MARK: - Enums
213
214#[rustfmt::skip] // workaround for broken #[rustfmt::skip::macros(make_zxing_enum)]
215make_zxing_enum!(ImageFormat { Lum, LumA, RGB, BGR, RGBA, ARGB, BGRA, ABGR });
216#[rustfmt::skip]
217make_zxing_enum!(ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI });
218#[rustfmt::skip]
219make_zxing_enum!(Binarizer { LocalAverage, GlobalHistogram, FixedThreshold, BoolCast });
220#[rustfmt::skip]
221make_zxing_enum!(TextMode { Plain, ECI, HRI, Escaped, Hex, HexECI });
222#[rustfmt::skip]
223make_zxing_enum!(EanAddOnSymbol { Ignore, Read, Require });
224
225#[rustfmt::skip]
226make_zxing_enum!(BarcodeFormat {
227	Invalid, None, All, AllReadable, AllCreatable, AllLinear, AllMatrix, AllGS1,
228	Codabar, Code39, PZN, Code93, Code128, ITF,
229	DataBar, DataBarOmni, DataBarStk, DataBarStkOmni, DataBarLtd, DataBarExp, DataBarExpStk,
230	EANUPC, EAN13, EAN8, EAN5, EAN2, ISBN, UPCA, UPCE,
231	Telepen, TelepenAlpha, TelepenNumeric, OtherBarcode, DXFilmEdge,
232	PDF417, CompactPDF417, MicroPDF417,
233	Aztec, AztecCode, AztecRune,
234	QRCode, QRCodeModel1, QRCodeModel2, MicroQRCode, RMQRCode,
235	DataMatrix, MaxiCode
236});
237
238impl Display for BarcodeFormat {
239	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
240		write!(f, "{}", unsafe { c2r_str(ZXing_BarcodeFormatToString(transmute(*self))) })
241	}
242}
243
244impl Display for ContentType {
245	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
246		write!(f, "{}", unsafe { c2r_str(ZXing_ContentTypeToString(transmute(*self))) })
247	}
248}
249
250impl BarcodeFormat {
251	pub fn symbology(self) -> BarcodeFormat {
252		unsafe { transmute(ZXing_BarcodeFormatSymbology(transmute(self))) }
253	}
254}
255
256// MARK: - BarcodeFormats
257
258#[derive(Clone, Debug, Default)]
259pub struct BarcodeFormats(pub Vec<BarcodeFormat>);
260
261impl BarcodeFormats {
262	pub fn as_slice(&self) -> &[BarcodeFormat] {
263		&self.0
264	}
265	pub fn is_empty(&self) -> bool {
266		self.0.is_empty()
267	}
268	pub fn len(&self) -> usize {
269		self.0.len()
270	}
271	pub fn contains(&self, f: BarcodeFormat) -> bool {
272		self.0.contains(&f)
273	}
274	pub fn iter(&self) -> std::slice::Iter<'_, BarcodeFormat> {
275		self.0.iter()
276	}
277
278	pub fn list(filter: BarcodeFormat) -> Self {
279		unsafe {
280			let mut size: c_int = 0;
281			let ptr = ZXing_BarcodeFormatsList(transmute(filter), &mut size) as *const BarcodeFormat;
282			if ptr.is_null() || size == 0 {
283				BarcodeFormats::default()
284			} else {
285				BarcodeFormats(slice::from_raw_parts(ptr, size as usize).to_vec())
286			}
287		}
288	}
289}
290
291impl PartialEq<[BarcodeFormat]> for BarcodeFormats {
292	fn eq(&self, other: &[BarcodeFormat]) -> bool {
293		self.0.as_slice() == other
294	}
295}
296
297impl PartialEq<BarcodeFormats> for [BarcodeFormat] {
298	fn eq(&self, other: &BarcodeFormats) -> bool {
299		self == other.0.as_slice()
300	}
301}
302
303impl Display for BarcodeFormats {
304	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
305		write!(f, "{}", unsafe {
306			c2r_str(ZXing_BarcodeFormatsToString(transmute(self.0.as_ptr()), self.0.len() as c_int))
307		})
308	}
309}
310
311// Add conversions so single values/slices/vecs can be passed conveniently
312// impl From<BarcodeFormat> for BarcodeFormats {
313// 	fn from(f: BarcodeFormat) -> Self {
314// 		BarcodeFormats(vec![f])
315// 	}
316// }
317impl From<Vec<BarcodeFormat>> for BarcodeFormats {
318	fn from(v: Vec<BarcodeFormat>) -> Self {
319		BarcodeFormats(v)
320	}
321}
322impl From<&[BarcodeFormat]> for BarcodeFormats {
323	fn from(s: &[BarcodeFormat]) -> Self {
324		BarcodeFormats(s.to_vec())
325	}
326}
327
328impl AsRef<[BarcodeFormat]> for BarcodeFormats {
329	fn as_ref(&self) -> &[BarcodeFormat] {
330		&self.0
331	}
332}
333
334impl AsRef<[BarcodeFormat]> for BarcodeFormat {
335	fn as_ref(&self) -> &[BarcodeFormat] {
336		std::slice::from_ref(self)
337	}
338}
339
340pub trait FromStr: Sized {
341	fn from_str(str: impl AsRef<str>) -> Result<Self, Error>;
342}
343
344impl FromStr for BarcodeFormat {
345	fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormat, Error> {
346		let cstr = CString::new(str.as_ref())?;
347		let fmt = unsafe { ZXing_BarcodeFormatFromString(cstr.as_ptr()) };
348		if fmt == ZXing_BarcodeFormat_Invalid {
349			last_error_or!(BarcodeFormat::Invalid)
350		} else {
351			Ok(unsafe { transmute(fmt) })
352		}
353	}
354}
355
356impl FromStr for BarcodeFormats {
357	fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormats, Error> {
358		let cstr = CString::new(str.as_ref())?;
359		let mut size: c_int = 0;
360		let ptr = unsafe { ZXing_BarcodeFormatsFromString(cstr.as_ptr(), &mut size) as *const BarcodeFormat };
361		if ptr.is_null() || size == 0 {
362			last_error_or!(BarcodeFormats::default())
363		} else {
364			Ok(BarcodeFormats(unsafe { slice::from_raw_parts(ptr, size as usize).to_vec() }))
365		}
366	}
367}
368
369// MARK: - ImageView
370
371#[derive(Debug, PartialEq)]
372struct ImageViewOwner<'a>(*mut ZXing_ImageView, PhantomData<&'a u8>);
373
374impl Drop for ImageViewOwner<'_> {
375	fn drop(&mut self) {
376		unsafe { ZXing_ImageView_delete(self.0) }
377	}
378}
379#[derive(Debug, Clone, PartialEq)]
380pub struct ImageView<'a>(Rc<ImageViewOwner<'a>>);
381
382impl<'a> From<&'a ImageView<'a>> for ImageView<'a> {
383	fn from(img: &'a ImageView) -> Self {
384		img.clone()
385	}
386}
387
388impl<'a> ImageView<'a> {
389	fn try_into_int<T: TryInto<c_int>>(val: T) -> Result<c_int, Error> {
390		val.try_into().map_err(|_| Error::InvalidInput("Could not convert Integer into c_int.".to_string()))
391	}
392
393	/// Constructs an ImageView from a raw pointer and the width/height (in pixels)
394	/// and row_stride/pix_stride (in bytes).
395	///
396	/// # Safety
397	///
398	/// The memory gets accessed inside the c++ library at random places between
399	/// `ptr` and `ptr + height * row_stride` or `ptr + width * pix_stride`.
400	/// Note that both the stride values could be negative, e.g. if the image
401	/// view is rotated.
402	pub unsafe fn from_ptr<T: TryInto<c_int>, U: TryInto<c_int>>(
403		ptr: *const u8,
404		width: T,
405		height: T,
406		format: ImageFormat,
407		row_stride: U,
408		pix_stride: U,
409	) -> Result<Self, Error> {
410		let iv = ZXing_ImageView_new(
411			ptr,
412			Self::try_into_int(width)?,
413			Self::try_into_int(height)?,
414			format as ZXing_ImageFormat,
415			Self::try_into_int(row_stride)?,
416			Self::try_into_int(pix_stride)?,
417		);
418		last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
419	}
420
421	pub fn from_slice<T: TryInto<c_int>>(data: &'a [u8], width: T, height: T, format: ImageFormat) -> Result<Self, Error> {
422		unsafe {
423			let iv = ZXing_ImageView_new_checked(
424				data.as_ptr(),
425				data.len() as c_int,
426				Self::try_into_int(width)?,
427				Self::try_into_int(height)?,
428				format as ZXing_ImageFormat,
429				0,
430				0,
431			);
432			last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
433		}
434	}
435
436	pub fn cropped(self, left: i32, top: i32, width: i32, height: i32) -> Self {
437		unsafe { ZXing_ImageView_crop((self.0).0, left, top, width, height) }
438		self
439	}
440
441	pub fn rotated(self, degree: i32) -> Self {
442		unsafe { ZXing_ImageView_rotate((self.0).0, degree) }
443		self
444	}
445}
446
447#[cfg(feature = "image")]
448use image;
449
450#[cfg(feature = "image")]
451impl<'a> From<&'a image::GrayImage> for ImageView<'a> {
452	fn from(img: &'a image::GrayImage) -> Self {
453		ImageView::from_slice(img.as_ref(), img.width(), img.height(), ImageFormat::Lum).unwrap()
454	}
455}
456
457#[cfg(feature = "image")]
458impl<'a> TryFrom<&'a image::DynamicImage> for ImageView<'a> {
459	type Error = Error;
460
461	fn try_from(img: &'a image::DynamicImage) -> Result<Self, Error> {
462		let format = match img {
463			image::DynamicImage::ImageLuma8(_) => Some(ImageFormat::Lum),
464			image::DynamicImage::ImageLumaA8(_) => Some(ImageFormat::LumA),
465			image::DynamicImage::ImageRgb8(_) => Some(ImageFormat::RGB),
466			image::DynamicImage::ImageRgba8(_) => Some(ImageFormat::RGBA),
467			_ => None,
468		};
469		match format {
470			Some(format) => Ok(ImageView::from_slice(img.as_bytes(), img.width(), img.height(), format)?),
471			None => Err(Error::InvalidInput("Invalid image format (must be either luma8|lumaA8|rgb8|rgba8)".to_string())),
472		}
473	}
474}
475
476// MARK: - Image, Error
477
478make_zxing_class!(Image, ZXing_Image);
479
480impl Image {
481	getter!(Image, width, transmute, i32);
482	getter!(Image, height, transmute, i32);
483	getter!(Image, format, transmute, ImageFormat);
484
485	pub fn data(&self) -> Vec<u8> {
486		let ptr = unsafe { ZXing_Image_data(self.0) };
487		if ptr.is_null() {
488			Vec::<u8>::new()
489		} else {
490			unsafe { std::slice::from_raw_parts(ptr, (self.width() * self.height()) as usize).to_vec() }
491		}
492	}
493}
494
495#[cfg(feature = "image")]
496impl From<&Image> for image::GrayImage {
497	fn from(img: &Image) -> image::GrayImage {
498		image::GrayImage::from_vec(img.width() as u32, img.height() as u32, img.data()).unwrap()
499	}
500}
501
502#[derive(Error, Debug, PartialEq)]
503pub enum BarcodeError {
504	#[error("")]
505	None(),
506
507	#[error("{0}")]
508	Checksum(String),
509
510	#[error("{0}")]
511	Format(String),
512
513	#[error("{0}")]
514	Unsupported(String),
515}
516
517// MARK: - Point, Position
518
519pub type PointI = ZXing_PointI;
520#[repr(C)]
521#[derive(Debug, Copy, Clone)]
522pub struct Position {
523	pub top_left: PointI,
524	pub top_right: PointI,
525	pub bottom_right: PointI,
526	pub bottom_left: PointI,
527}
528
529impl Display for PointI {
530	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
531		write!(f, "{}x{}", self.x, self.y)
532	}
533}
534
535impl Display for Position {
536	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
537		write!(f, "{}", unsafe {
538			c2r_str(ZXing_PositionToString(*(self as *const Position as *const ZXing_Position)))
539		})
540	}
541}
542
543// MARK: - Barcode
544
545make_zxing_class!(Barcode, ZXing_Barcode);
546
547impl Barcode {
548	getter!(Barcode, isValid, transmute, bool);
549	getter!(Barcode, format, transmute, BarcodeFormat);
550	getter!(Barcode, symbology, transmute, BarcodeFormat);
551	getter!(Barcode, contentType, transmute, ContentType);
552	getter!(Barcode, text, c2r_str, String);
553	getter!(Barcode, symbologyIdentifier, c2r_str, String);
554	getter!(Barcode, position, transmute, Position);
555	getter!(Barcode, orientation, transmute, i32);
556	getter!(Barcode, hasECI, has_eci, transmute, bool);
557	getter!(Barcode, isInverted, transmute, bool);
558	getter!(Barcode, isMirrored, transmute, bool);
559	getter!(Barcode, lineCount, transmute, i32);
560	getter!(Barcode, sequenceSize, transmute, i32);
561	getter!(Barcode, sequenceIndex, transmute, i32);
562	getter!(Barcode, sequenceId, c2r_str, String);
563
564	pub fn bytes(&self) -> Vec<u8> {
565		let mut len: c_int = 0;
566		unsafe { c2r_vec(ZXing_Barcode_bytes(self.0, &mut len), len) }
567	}
568	pub fn bytes_eci(&self) -> Vec<u8> {
569		let mut len: c_int = 0;
570		unsafe { c2r_vec(ZXing_Barcode_bytesECI(self.0, &mut len), len) }
571	}
572
573	pub fn extra(&self) -> String {
574		unsafe { c2r_str(ZXing_Barcode_extra(self.0, null())) }
575	}
576
577	pub fn extra_with_key(&self, key: impl AsRef<str>) -> String {
578		let cstr = CString::new(key.as_ref()).unwrap();
579		unsafe { c2r_str(ZXing_Barcode_extra(self.0, cstr.as_ptr())) }
580	}
581
582	pub fn error(&self) -> BarcodeError {
583		let error_type = unsafe { ZXing_Barcode_errorType(self.0) };
584		let error_msg = unsafe { c2r_str(ZXing_Barcode_errorMsg(self.0)) };
585		#[allow(non_upper_case_globals)]
586		match error_type {
587			ZXing_ErrorType_None => BarcodeError::None(),
588			ZXing_ErrorType_Format => BarcodeError::Format(error_msg),
589			ZXing_ErrorType_Checksum => BarcodeError::Checksum(error_msg),
590			ZXing_ErrorType_Unsupported => BarcodeError::Unsupported(error_msg),
591			_ => panic!("Internal error: invalid ZXing_ErrorType"),
592		}
593	}
594
595	pub fn to_svg_with(&self, opts: &BarcodeWriter) -> Result<String, Error> {
596		let str = unsafe { ZXing_WriteBarcodeToSVG(self.0, opts.0) };
597		last_error_if_null_or!(str, c2r_str(str))
598	}
599
600	pub fn to_svg(&self) -> Result<String, Error> {
601		self.to_svg_with(&BarcodeWriter::default())
602	}
603
604	pub fn to_image_with(&self, opts: &BarcodeWriter) -> Result<Image, Error> {
605		let img = unsafe { ZXing_WriteBarcodeToImage(self.0, opts.0) };
606		last_error_if_null_or!(img, Image(img))
607	}
608
609	pub fn to_image(&self) -> Result<Image, Error> {
610		self.to_image_with(&BarcodeWriter::default())
611	}
612}
613
614// MARK: - BarcodeReader
615
616make_zxing_class_with_default!(BarcodeReader, ZXing_ReaderOptions);
617
618impl BarcodeReader {
619	property!(ReaderOptions, TryHarder, bool);
620	property!(ReaderOptions, TryRotate, bool);
621	property!(ReaderOptions, TryInvert, bool);
622	property!(ReaderOptions, TryDownscale, bool);
623	property!(ReaderOptions, IsPure, bool);
624	property!(ReaderOptions, ValidateOptionalChecksum, bool);
625	property!(ReaderOptions, ReturnErrors, bool);
626	property!(ReaderOptions, Binarizer, Binarizer);
627	property!(ReaderOptions, EanAddOnSymbol, EanAddOnSymbol);
628	property!(ReaderOptions, TextMode, TextMode);
629	property!(ReaderOptions, MinLineCount, i32);
630	property!(ReaderOptions, MaxNumberOfSymbols, i32);
631
632	pub fn formats(self, v: impl AsRef<[BarcodeFormat]>) -> Self {
633		unsafe { ZXing_ReaderOptions_setFormats(self.0, transmute(v.as_ref().as_ptr()), v.as_ref().len() as c_int) };
634		self
635	}
636
637	pub fn set_formats(&mut self, v: impl AsRef<[BarcodeFormat]>) -> &mut Self {
638		unsafe { ZXing_ReaderOptions_setFormats(self.0, transmute(v.as_ref().as_ptr()), v.as_ref().len() as c_int) };
639		self
640	}
641
642	pub fn get_formats(&self) -> BarcodeFormats {
643		unsafe {
644			let mut size: c_int = 0;
645			let ptr = ZXing_ReaderOptions_getFormats(self.0, &mut size) as *const BarcodeFormat;
646			if ptr.is_null() || size == 0 {
647				BarcodeFormats::default()
648			} else {
649				BarcodeFormats(slice::from_raw_parts(ptr, size as usize).to_vec())
650			}
651		}
652	}
653
654	pub fn from<'a, IV>(&self, image: IV) -> Result<Vec<Barcode>, Error>
655	where
656		IV: TryInto<ImageView<'a>>,
657		IV::Error: Into<Error>,
658	{
659		let iv_: ImageView = image.try_into().map_err(Into::into)?;
660		unsafe {
661			let results = ZXing_ReadBarcodes((iv_.0).0, self.0);
662			if !results.is_null() {
663				let size = ZXing_Barcodes_size(results);
664				let mut vec = Vec::<Barcode>::with_capacity(size as usize);
665				for i in 0..size {
666					vec.push(Barcode(ZXing_Barcodes_move(results, i)));
667				}
668				ZXing_Barcodes_delete(results);
669				Ok(vec)
670			} else {
671				Err(last_error())
672			}
673		}
674	}
675}
676
677// MARK: - BarcodeCreator
678
679make_zxing_class!(BarcodeCreator, ZXing_CreatorOptions);
680
681impl BarcodeCreator {
682	pub fn new(format: BarcodeFormat) -> Self {
683		unsafe { BarcodeCreator(ZXing_CreatorOptions_new(format as ZXing_BarcodeFormat)) }
684	}
685
686	property!(CreatorOptions, Options, String);
687
688	pub fn from_str(&self, str: impl AsRef<str>) -> Result<Barcode, Error> {
689		let cstr = CString::new(str.as_ref())?;
690		let bc = unsafe { ZXing_CreateBarcodeFromText(cstr.as_ptr(), 0, self.0) };
691		last_error_if_null_or!(bc, Barcode(bc))
692	}
693
694	pub fn from_slice(&self, data: impl AsRef<[u8]>) -> Result<Barcode, Error> {
695		let data = data.as_ref();
696		let bc = unsafe { ZXing_CreateBarcodeFromBytes(data.as_ptr() as *const c_void, data.len() as i32, self.0) };
697		last_error_if_null_or!(bc, Barcode(bc))
698	}
699}
700
701// MARK: - BarcodeWriter
702
703make_zxing_class_with_default!(BarcodeWriter, ZXing_WriterOptions);
704
705impl BarcodeWriter {
706	property!(WriterOptions, Scale, i32);
707	property!(WriterOptions, Rotate, i32);
708	property!(WriterOptions, AddHRT, add_hrt, bool);
709	property!(WriterOptions, AddQuietZones, bool);
710}
711
712// MARK: - Convenience Functions
713
714pub fn read() -> BarcodeReader {
715	BarcodeReader::default()
716}
717
718pub fn create(format: BarcodeFormat) -> BarcodeCreator {
719	BarcodeCreator::new(format)
720}
721
722pub fn write() -> BarcodeWriter {
723	BarcodeWriter::default()
724}