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, OtherBarcode, DXFilmEdge,
231	PDF417, CompactPDF417, MicroPDF417,
232	Aztec, AztecCode, AztecRune,
233	QRCode, QRCodeModel1, QRCodeModel2, MicroQRCode, RMQRCode,
234	DataMatrix, MaxiCode
235});
236
237impl Display for BarcodeFormat {
238	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
239		write!(f, "{}", unsafe { c2r_str(ZXing_BarcodeFormatToString(transmute(*self))) })
240	}
241}
242
243impl Display for ContentType {
244	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245		write!(f, "{}", unsafe { c2r_str(ZXing_ContentTypeToString(transmute(*self))) })
246	}
247}
248
249impl BarcodeFormat {
250	pub fn symbology(self) -> BarcodeFormat {
251		unsafe { transmute(ZXing_BarcodeFormatSymbology(transmute(self))) }
252	}
253}
254
255// MARK: - BarcodeFormats
256
257#[derive(Clone, Debug, Default)]
258pub struct BarcodeFormats(pub Vec<BarcodeFormat>);
259
260impl BarcodeFormats {
261	pub fn as_slice(&self) -> &[BarcodeFormat] {
262		&self.0
263	}
264	pub fn is_empty(&self) -> bool {
265		self.0.is_empty()
266	}
267	pub fn len(&self) -> usize {
268		self.0.len()
269	}
270	pub fn contains(&self, f: BarcodeFormat) -> bool {
271		self.0.contains(&f)
272	}
273	pub fn iter(&self) -> std::slice::Iter<'_, BarcodeFormat> {
274		self.0.iter()
275	}
276
277	pub fn list(filter: BarcodeFormat) -> Self {
278		unsafe {
279			let mut size: c_int = 0;
280			let ptr = ZXing_BarcodeFormatsList(transmute(filter), &mut size) as *const BarcodeFormat;
281			if ptr.is_null() || size == 0 {
282				BarcodeFormats::default()
283			} else {
284				BarcodeFormats(slice::from_raw_parts(ptr, size as usize).to_vec())
285			}
286		}
287	}
288}
289
290impl PartialEq<[BarcodeFormat]> for BarcodeFormats {
291	fn eq(&self, other: &[BarcodeFormat]) -> bool {
292		self.0.as_slice() == other
293	}
294}
295
296impl PartialEq<BarcodeFormats> for [BarcodeFormat] {
297	fn eq(&self, other: &BarcodeFormats) -> bool {
298		self == other.0.as_slice()
299	}
300}
301
302impl Display for BarcodeFormats {
303	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
304		write!(f, "{}", unsafe {
305			c2r_str(ZXing_BarcodeFormatsToString(transmute(self.0.as_ptr()), self.0.len() as c_int))
306		})
307	}
308}
309
310// Add conversions so single values/slices/vecs can be passed conveniently
311// impl From<BarcodeFormat> for BarcodeFormats {
312// 	fn from(f: BarcodeFormat) -> Self {
313// 		BarcodeFormats(vec![f])
314// 	}
315// }
316impl From<Vec<BarcodeFormat>> for BarcodeFormats {
317	fn from(v: Vec<BarcodeFormat>) -> Self {
318		BarcodeFormats(v)
319	}
320}
321impl From<&[BarcodeFormat]> for BarcodeFormats {
322	fn from(s: &[BarcodeFormat]) -> Self {
323		BarcodeFormats(s.to_vec())
324	}
325}
326
327impl AsRef<[BarcodeFormat]> for BarcodeFormats {
328	fn as_ref(&self) -> &[BarcodeFormat] {
329		&self.0
330	}
331}
332
333impl AsRef<[BarcodeFormat]> for BarcodeFormat {
334	fn as_ref(&self) -> &[BarcodeFormat] {
335		std::slice::from_ref(self)
336	}
337}
338
339pub trait FromStr: Sized {
340	fn from_str(str: impl AsRef<str>) -> Result<Self, Error>;
341}
342
343impl FromStr for BarcodeFormat {
344	fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormat, Error> {
345		let cstr = CString::new(str.as_ref())?;
346		let fmt = unsafe { ZXing_BarcodeFormatFromString(cstr.as_ptr()) };
347		if fmt == ZXing_BarcodeFormat_Invalid {
348			last_error_or!(BarcodeFormat::Invalid)
349		} else {
350			Ok(unsafe { transmute(fmt) })
351		}
352	}
353}
354
355impl FromStr for BarcodeFormats {
356	fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormats, Error> {
357		let cstr = CString::new(str.as_ref())?;
358		let mut size: c_int = 0;
359		let ptr = unsafe { ZXing_BarcodeFormatsFromString(cstr.as_ptr(), &mut size) as *const BarcodeFormat };
360		if ptr.is_null() || size == 0 {
361			last_error_or!(BarcodeFormats::default())
362		} else {
363			Ok(BarcodeFormats(unsafe { slice::from_raw_parts(ptr, size as usize).to_vec() }))
364		}
365	}
366}
367
368// MARK: - ImageView
369
370#[derive(Debug, PartialEq)]
371struct ImageViewOwner<'a>(*mut ZXing_ImageView, PhantomData<&'a u8>);
372
373impl Drop for ImageViewOwner<'_> {
374	fn drop(&mut self) {
375		unsafe { ZXing_ImageView_delete(self.0) }
376	}
377}
378#[derive(Debug, Clone, PartialEq)]
379pub struct ImageView<'a>(Rc<ImageViewOwner<'a>>);
380
381impl<'a> From<&'a ImageView<'a>> for ImageView<'a> {
382	fn from(img: &'a ImageView) -> Self {
383		img.clone()
384	}
385}
386
387impl<'a> ImageView<'a> {
388	fn try_into_int<T: TryInto<c_int>>(val: T) -> Result<c_int, Error> {
389		val.try_into().map_err(|_| Error::InvalidInput("Could not convert Integer into c_int.".to_string()))
390	}
391
392	/// Constructs an ImageView from a raw pointer and the width/height (in pixels)
393	/// and row_stride/pix_stride (in bytes).
394	///
395	/// # Safety
396	///
397	/// The memory gets accessed inside the c++ library at random places between
398	/// `ptr` and `ptr + height * row_stride` or `ptr + width * pix_stride`.
399	/// Note that both the stride values could be negative, e.g. if the image
400	/// view is rotated.
401	pub unsafe fn from_ptr<T: TryInto<c_int>, U: TryInto<c_int>>(
402		ptr: *const u8,
403		width: T,
404		height: T,
405		format: ImageFormat,
406		row_stride: U,
407		pix_stride: U,
408	) -> Result<Self, Error> {
409		let iv = ZXing_ImageView_new(
410			ptr,
411			Self::try_into_int(width)?,
412			Self::try_into_int(height)?,
413			format as ZXing_ImageFormat,
414			Self::try_into_int(row_stride)?,
415			Self::try_into_int(pix_stride)?,
416		);
417		last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
418	}
419
420	pub fn from_slice<T: TryInto<c_int>>(data: &'a [u8], width: T, height: T, format: ImageFormat) -> Result<Self, Error> {
421		unsafe {
422			let iv = ZXing_ImageView_new_checked(
423				data.as_ptr(),
424				data.len() as c_int,
425				Self::try_into_int(width)?,
426				Self::try_into_int(height)?,
427				format as ZXing_ImageFormat,
428				0,
429				0,
430			);
431			last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
432		}
433	}
434
435	pub fn cropped(self, left: i32, top: i32, width: i32, height: i32) -> Self {
436		unsafe { ZXing_ImageView_crop((self.0).0, left, top, width, height) }
437		self
438	}
439
440	pub fn rotated(self, degree: i32) -> Self {
441		unsafe { ZXing_ImageView_rotate((self.0).0, degree) }
442		self
443	}
444}
445
446#[cfg(feature = "image")]
447use image;
448
449#[cfg(feature = "image")]
450impl<'a> From<&'a image::GrayImage> for ImageView<'a> {
451	fn from(img: &'a image::GrayImage) -> Self {
452		ImageView::from_slice(img.as_ref(), img.width(), img.height(), ImageFormat::Lum).unwrap()
453	}
454}
455
456#[cfg(feature = "image")]
457impl<'a> TryFrom<&'a image::DynamicImage> for ImageView<'a> {
458	type Error = Error;
459
460	fn try_from(img: &'a image::DynamicImage) -> Result<Self, Error> {
461		let format = match img {
462			image::DynamicImage::ImageLuma8(_) => Some(ImageFormat::Lum),
463			image::DynamicImage::ImageLumaA8(_) => Some(ImageFormat::LumA),
464			image::DynamicImage::ImageRgb8(_) => Some(ImageFormat::RGB),
465			image::DynamicImage::ImageRgba8(_) => Some(ImageFormat::RGBA),
466			_ => None,
467		};
468		match format {
469			Some(format) => Ok(ImageView::from_slice(img.as_bytes(), img.width(), img.height(), format)?),
470			None => Err(Error::InvalidInput("Invalid image format (must be either luma8|lumaA8|rgb8|rgba8)".to_string())),
471		}
472	}
473}
474
475// MARK: - Image, Error
476
477make_zxing_class!(Image, ZXing_Image);
478
479impl Image {
480	getter!(Image, width, transmute, i32);
481	getter!(Image, height, transmute, i32);
482	getter!(Image, format, transmute, ImageFormat);
483
484	pub fn data(&self) -> Vec<u8> {
485		let ptr = unsafe { ZXing_Image_data(self.0) };
486		if ptr.is_null() {
487			Vec::<u8>::new()
488		} else {
489			unsafe { std::slice::from_raw_parts(ptr, (self.width() * self.height()) as usize).to_vec() }
490		}
491	}
492}
493
494#[cfg(feature = "image")]
495impl From<&Image> for image::GrayImage {
496	fn from(img: &Image) -> image::GrayImage {
497		image::GrayImage::from_vec(img.width() as u32, img.height() as u32, img.data()).unwrap()
498	}
499}
500
501#[derive(Error, Debug, PartialEq)]
502pub enum BarcodeError {
503	#[error("")]
504	None(),
505
506	#[error("{0}")]
507	Checksum(String),
508
509	#[error("{0}")]
510	Format(String),
511
512	#[error("{0}")]
513	Unsupported(String),
514}
515
516// MARK: - Point, Position
517
518pub type PointI = ZXing_PointI;
519#[repr(C)]
520#[derive(Debug, Copy, Clone)]
521pub struct Position {
522	pub top_left: PointI,
523	pub top_right: PointI,
524	pub bottom_right: PointI,
525	pub bottom_left: PointI,
526}
527
528impl Display for PointI {
529	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
530		write!(f, "{}x{}", self.x, self.y)
531	}
532}
533
534impl Display for Position {
535	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
536		write!(f, "{}", unsafe {
537			c2r_str(ZXing_PositionToString(*(self as *const Position as *const ZXing_Position)))
538		})
539	}
540}
541
542// MARK: - Barcode
543
544make_zxing_class!(Barcode, ZXing_Barcode);
545
546impl Barcode {
547	getter!(Barcode, isValid, transmute, bool);
548	getter!(Barcode, format, transmute, BarcodeFormat);
549	getter!(Barcode, symbology, transmute, BarcodeFormat);
550	getter!(Barcode, contentType, transmute, ContentType);
551	getter!(Barcode, text, c2r_str, String);
552	getter!(Barcode, symbologyIdentifier, c2r_str, String);
553	getter!(Barcode, position, transmute, Position);
554	getter!(Barcode, orientation, transmute, i32);
555	getter!(Barcode, hasECI, has_eci, transmute, bool);
556	getter!(Barcode, isInverted, transmute, bool);
557	getter!(Barcode, isMirrored, transmute, bool);
558	getter!(Barcode, lineCount, transmute, i32);
559	getter!(Barcode, sequenceSize, transmute, i32);
560	getter!(Barcode, sequenceIndex, transmute, i32);
561	getter!(Barcode, sequenceId, c2r_str, String);
562
563	pub fn bytes(&self) -> Vec<u8> {
564		let mut len: c_int = 0;
565		unsafe { c2r_vec(ZXing_Barcode_bytes(self.0, &mut len), len) }
566	}
567	pub fn bytes_eci(&self) -> Vec<u8> {
568		let mut len: c_int = 0;
569		unsafe { c2r_vec(ZXing_Barcode_bytesECI(self.0, &mut len), len) }
570	}
571
572	pub fn extra(&self) -> String {
573		unsafe { c2r_str(ZXing_Barcode_extra(self.0, null())) }
574	}
575
576	pub fn extra_with_key(&self, key: impl AsRef<str>) -> String {
577		let cstr = CString::new(key.as_ref()).unwrap();
578		unsafe { c2r_str(ZXing_Barcode_extra(self.0, cstr.as_ptr())) }
579	}
580
581	pub fn error(&self) -> BarcodeError {
582		let error_type = unsafe { ZXing_Barcode_errorType(self.0) };
583		let error_msg = unsafe { c2r_str(ZXing_Barcode_errorMsg(self.0)) };
584		#[allow(non_upper_case_globals)]
585		match error_type {
586			ZXing_ErrorType_None => BarcodeError::None(),
587			ZXing_ErrorType_Format => BarcodeError::Format(error_msg),
588			ZXing_ErrorType_Checksum => BarcodeError::Checksum(error_msg),
589			ZXing_ErrorType_Unsupported => BarcodeError::Unsupported(error_msg),
590			_ => panic!("Internal error: invalid ZXing_ErrorType"),
591		}
592	}
593
594	pub fn to_svg_with(&self, opts: &BarcodeWriter) -> Result<String, Error> {
595		let str = unsafe { ZXing_WriteBarcodeToSVG(self.0, opts.0) };
596		last_error_if_null_or!(str, c2r_str(str))
597	}
598
599	pub fn to_svg(&self) -> Result<String, Error> {
600		self.to_svg_with(&BarcodeWriter::default())
601	}
602
603	pub fn to_image_with(&self, opts: &BarcodeWriter) -> Result<Image, Error> {
604		let img = unsafe { ZXing_WriteBarcodeToImage(self.0, opts.0) };
605		last_error_if_null_or!(img, Image(img))
606	}
607
608	pub fn to_image(&self) -> Result<Image, Error> {
609		self.to_image_with(&BarcodeWriter::default())
610	}
611}
612
613// MARK: - BarcodeReader
614
615make_zxing_class_with_default!(BarcodeReader, ZXing_ReaderOptions);
616
617impl BarcodeReader {
618	property!(ReaderOptions, TryHarder, bool);
619	property!(ReaderOptions, TryRotate, bool);
620	property!(ReaderOptions, TryInvert, bool);
621	property!(ReaderOptions, TryDownscale, bool);
622	property!(ReaderOptions, IsPure, bool);
623	property!(ReaderOptions, ValidateOptionalChecksum, bool);
624	property!(ReaderOptions, ReturnErrors, bool);
625	property!(ReaderOptions, Binarizer, Binarizer);
626	property!(ReaderOptions, EanAddOnSymbol, EanAddOnSymbol);
627	property!(ReaderOptions, TextMode, TextMode);
628	property!(ReaderOptions, MinLineCount, i32);
629	property!(ReaderOptions, MaxNumberOfSymbols, i32);
630
631	pub fn formats(self, v: impl AsRef<[BarcodeFormat]>) -> Self {
632		unsafe { ZXing_ReaderOptions_setFormats(self.0, transmute(v.as_ref().as_ptr()), v.as_ref().len() as c_int) };
633		self
634	}
635
636	pub fn set_formats(&mut self, v: impl AsRef<[BarcodeFormat]>) -> &mut Self {
637		unsafe { ZXing_ReaderOptions_setFormats(self.0, transmute(v.as_ref().as_ptr()), v.as_ref().len() as c_int) };
638		self
639	}
640
641	pub fn get_formats(&self) -> BarcodeFormats {
642		unsafe {
643			let mut size: c_int = 0;
644			let ptr = ZXing_ReaderOptions_getFormats(self.0, &mut size) as *const BarcodeFormat;
645			if ptr.is_null() || size == 0 {
646				BarcodeFormats::default()
647			} else {
648				BarcodeFormats(slice::from_raw_parts(ptr, size as usize).to_vec())
649			}
650		}
651	}
652
653	pub fn from<'a, IV>(&self, image: IV) -> Result<Vec<Barcode>, Error>
654	where
655		IV: TryInto<ImageView<'a>>,
656		IV::Error: Into<Error>,
657	{
658		let iv_: ImageView = image.try_into().map_err(Into::into)?;
659		unsafe {
660			let results = ZXing_ReadBarcodes((iv_.0).0, self.0);
661			if !results.is_null() {
662				let size = ZXing_Barcodes_size(results);
663				let mut vec = Vec::<Barcode>::with_capacity(size as usize);
664				for i in 0..size {
665					vec.push(Barcode(ZXing_Barcodes_move(results, i)));
666				}
667				ZXing_Barcodes_delete(results);
668				Ok(vec)
669			} else {
670				Err(last_error())
671			}
672		}
673	}
674}
675
676// MARK: - BarcodeCreator
677
678make_zxing_class!(BarcodeCreator, ZXing_CreatorOptions);
679
680impl BarcodeCreator {
681	pub fn new(format: BarcodeFormat) -> Self {
682		unsafe { BarcodeCreator(ZXing_CreatorOptions_new(format as ZXing_BarcodeFormat)) }
683	}
684
685	property!(CreatorOptions, Options, String);
686
687	pub fn from_str(&self, str: impl AsRef<str>) -> Result<Barcode, Error> {
688		let cstr = CString::new(str.as_ref())?;
689		let bc = unsafe { ZXing_CreateBarcodeFromText(cstr.as_ptr(), 0, self.0) };
690		last_error_if_null_or!(bc, Barcode(bc))
691	}
692
693	pub fn from_slice(&self, data: impl AsRef<[u8]>) -> Result<Barcode, Error> {
694		let data = data.as_ref();
695		let bc = unsafe { ZXing_CreateBarcodeFromBytes(data.as_ptr() as *const c_void, data.len() as i32, self.0) };
696		last_error_if_null_or!(bc, Barcode(bc))
697	}
698}
699
700// MARK: - BarcodeWriter
701
702make_zxing_class_with_default!(BarcodeWriter, ZXing_WriterOptions);
703
704impl BarcodeWriter {
705	property!(WriterOptions, Scale, i32);
706	property!(WriterOptions, Rotate, i32);
707	property!(WriterOptions, AddHRT, add_hrt, bool);
708	property!(WriterOptions, AddQuietZones, bool);
709}
710
711// MARK: - Convenience Functions
712
713pub fn read() -> BarcodeReader {
714	BarcodeReader::default()
715}
716
717pub fn create(format: BarcodeFormat) -> BarcodeCreator {
718	BarcodeCreator::new(format)
719}
720
721pub fn write() -> BarcodeWriter {
722	BarcodeWriter::default()
723}