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