1#![allow(unknown_lints)] #![allow(unused_unsafe)]
8#![allow(clippy::useless_transmute)]
9#![allow(clippy::redundant_closure_call)]
10#![allow(clippy::missing_transmute_annotations)] mod 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 flagset::{flags, FlagSet};
25use paste::paste;
26use std::ffi::{c_char, c_int, c_uint, c_void, CStr, CString, NulError};
27use std::fmt::{Display, Formatter};
28use std::marker::PhantomData;
29use std::mem::transmute;
30use std::rc::Rc;
31use thiserror::Error;
32
33#[derive(Error, Debug)]
34pub enum Error {
35 #[error("{0}")]
36 InvalidInput(String),
37
38 #[error("NulError from CString::new")]
39 NulError(#[from] NulError),
40 }
53
54impl From<std::convert::Infallible> for Error {
56 fn from(_: std::convert::Infallible) -> Self {
57 unreachable!()
58 }
59}
60
61fn c2r_str(str: *mut c_char) -> String {
62 let mut res = String::new();
63 if !str.is_null() {
64 unsafe { res = CStr::from_ptr(str).to_string_lossy().to_string() };
65 unsafe { ZXing_free(str as *mut c_void) };
66 }
67 res
68}
69
70fn c2r_vec(buf: *mut u8, len: c_int) -> Vec<u8> {
71 let mut res = Vec::<u8>::new();
72 if !buf.is_null() && len > 0 {
73 unsafe { res = std::slice::from_raw_parts(buf, len as usize).to_vec() };
74 unsafe { ZXing_free(buf as *mut c_void) };
75 }
76 res
77}
78
79fn last_error() -> Error {
80 match unsafe { ZXing_LastErrorMsg().as_mut() } {
81 None => panic!("Internal error: ZXing_LastErrorMsg() returned NULL"),
82 Some(error) => Error::InvalidInput(c2r_str(error)),
83 }
84}
85
86macro_rules! last_error_or {
87 ($expr:expr) => {
88 match unsafe { ZXing_LastErrorMsg().as_mut() } {
89 None => Ok($expr),
90 Some(error) => Err(Error::InvalidInput(c2r_str(error))),
91 }
92 };
93}
94
95macro_rules! last_error_if_null_or {
96 ($ptr:ident, $expr:expr) => {
97 match $ptr.is_null() {
98 true => Err(last_error()),
99 false => Ok($expr),
100 }
101 };
102}
103
104macro_rules! make_zxing_class {
105 ($r_class:ident, $c_class:ident) => {
106 paste! {
107 pub struct $r_class(*mut $c_class);
108
109 impl Drop for $r_class {
110 fn drop(&mut self) {
111 unsafe { [<$c_class _delete>](self.0) }
112 }
113 }
114 }
115 };
116}
117
118macro_rules! make_zxing_class_with_default {
119 ($r_class:ident, $c_class:ident) => {
120 make_zxing_class!($r_class, $c_class);
121 paste! {
122 impl $r_class {
123 pub fn new() -> Self {
124 unsafe { $r_class([<$c_class _new>]()) }
125 }
126 }
127
128 impl Default for $r_class {
129 fn default() -> Self {
130 Self::new()
131 }
132 }
133 }
134 };
135}
136
137macro_rules! getter {
138 ($class:ident, $c_name:ident, $r_name:ident, $conv:expr, $type:ty) => {
139 pub fn $r_name(&self) -> $type {
140 paste! { unsafe { $conv([<ZXing_ $class _ $c_name>](self.0)) } }
141 }
142 };
143 ($class:ident, $c_name:ident, $conv:expr, $type:ty) => {
144 paste! { getter! { $class, $c_name, [<$c_name:snake>], $conv, $type } }
145 };
146}
147
148macro_rules! property {
149 ($class:ident, $c_name:ident, $r_name:ident, String) => {
150 pub fn $r_name(self, v: impl AsRef<str>) -> Self {
151 let cstr = CString::new(v.as_ref()).unwrap();
152 paste! { unsafe { [<ZXing_ $class _set $c_name>](self.0, cstr.as_ptr()) } };
153 self
154 }
155
156 paste! {
157 pub fn [<set_ $r_name>](&mut self, v : impl AsRef<str>) -> &mut Self {
158 let cstr = CString::new(v.as_ref()).unwrap();
159 unsafe { [<ZXing_ $class _set $c_name>](self.0, cstr.as_ptr()) };
160 self
161 }
162
163 pub fn [<get_ $r_name>](&self) -> String {
164 unsafe { c2r_str([<ZXing_ $class _get $c_name>](self.0)) }
165 }
166 }
167 };
168
169 ($class:ident, $c_name:ident, $r_name:ident, $type:ty) => {
170 pub fn $r_name(self, v: impl Into<$type>) -> Self {
171 paste! { unsafe { [<ZXing_ $class _set $c_name>](self.0, transmute(v.into())) } };
172 self
173 }
174
175 paste! {
176 pub fn [<set_ $r_name>](&mut self, v : impl Into<$type>) -> &mut Self {
177 unsafe { [<ZXing_ $class _set $c_name>](self.0, transmute(v.into())) };
178 self
179 }
180
181 pub fn [<get_ $r_name>](&self) -> $type {
182 unsafe { transmute([<ZXing_ $class _get $c_name>](self.0)) }
183 }
184 }
185 };
186
187 ($class:ident, $c_name:ident, $type:ty) => {
188 paste! { property! { $class, $c_name, [<$c_name:snake>], $type } }
189 };
190}
191
192macro_rules! make_zxing_enum {
193 ($name:ident { $($field:ident),* }) => {
194 #[repr(u32)]
195 #[derive(Debug, Copy, Clone, PartialEq)]
196 pub enum $name {
197 $($field = paste! { [<ZXing_ $name _ $field>] },)*
198 }
199 }
200}
201
202macro_rules! make_zxing_flags {
203 ($name:ident { $($field:ident),* }) => {
204 flags! {
205 #[repr(u32)]
206 pub enum $name: c_uint {
207 $($field = paste! { [<ZXing_ $name _ $field>] },)*
208 }
209 }
210 }
211}
212#[rustfmt::skip] make_zxing_enum!(ImageFormat { Lum, LumA, RGB, BGR, RGBA, ARGB, BGRA, ABGR });
214#[rustfmt::skip]
215make_zxing_enum!(ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI });
216#[rustfmt::skip]
217make_zxing_enum!(Binarizer { LocalAverage, GlobalHistogram, FixedThreshold, BoolCast });
218#[rustfmt::skip]
219make_zxing_enum!(TextMode { Plain, ECI, HRI, Hex, Escaped });
220#[rustfmt::skip]
221make_zxing_enum!(EanAddOnSymbol { Ignore, Read, Require });
222
223#[rustfmt::skip]
224make_zxing_flags!(BarcodeFormat {
225 None, Aztec, Codabar, Code39, Code93, Code128, DataBar, DataBarExpanded, DataBarLimited, DataMatrix,
226 EAN8, EAN13, ITF, MaxiCode, PDF417, QRCode, UPCA, UPCE, MicroQRCode, RMQRCode, DXFilmEdge,
227 LinearCodes, MatrixCodes, Any
228});
229
230impl Display for BarcodeFormat {
231 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
232 write!(f, "{}", unsafe { c2r_str(ZXing_BarcodeFormatToString(BarcodeFormats::from(*self).bits())) })
233 }
234}
235
236impl Display for ContentType {
237 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238 write!(f, "{}", unsafe { c2r_str(ZXing_ContentTypeToString(transmute(*self))) })
239 }
240}
241
242pub type BarcodeFormats = FlagSet<BarcodeFormat>;
243
244pub trait FromStr: Sized {
245 fn from_str(str: impl AsRef<str>) -> Result<Self, Error>;
246}
247
248impl FromStr for BarcodeFormat {
249 fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormat, Error> {
250 let cstr = CString::new(str.as_ref())?;
251 let res = unsafe { BarcodeFormats::new_unchecked(ZXing_BarcodeFormatFromString(cstr.as_ptr())) };
252 match res.bits() {
253 u32::MAX => last_error_or!(BarcodeFormat::None),
254 _ => Ok(res.into_iter().last().unwrap()),
255 }
256 }
257}
258
259impl FromStr for BarcodeFormats {
260 fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormats, Error> {
261 let cstr = CString::new(str.as_ref())?;
262 let res = unsafe { BarcodeFormats::new_unchecked(ZXing_BarcodeFormatsFromString(cstr.as_ptr())) };
263 match res.bits() {
264 u32::MAX => last_error_or!(BarcodeFormats::default()),
265 0 => Ok(BarcodeFormats::full()),
266 _ => Ok(res),
267 }
268 }
269}
270
271#[derive(Debug, PartialEq)]
272struct ImageViewOwner<'a>(*mut ZXing_ImageView, PhantomData<&'a u8>);
273
274impl Drop for ImageViewOwner<'_> {
275 fn drop(&mut self) {
276 unsafe { ZXing_ImageView_delete(self.0) }
277 }
278}
279#[derive(Debug, Clone, PartialEq)]
280pub struct ImageView<'a>(Rc<ImageViewOwner<'a>>);
281
282impl<'a> From<&'a ImageView<'a>> for ImageView<'a> {
283 fn from(img: &'a ImageView) -> Self {
284 img.clone()
285 }
286}
287
288impl<'a> ImageView<'a> {
289 fn try_into_int<T: TryInto<c_int>>(val: T) -> Result<c_int, Error> {
290 val.try_into().map_err(|_| Error::InvalidInput("Could not convert Integer into c_int.".to_string()))
291 }
292
293 pub unsafe fn from_ptr<T: TryInto<c_int>, U: TryInto<c_int>>(
303 ptr: *const u8,
304 width: T,
305 height: T,
306 format: ImageFormat,
307 row_stride: U,
308 pix_stride: U,
309 ) -> Result<Self, Error> {
310 let iv = ZXing_ImageView_new(
311 ptr,
312 Self::try_into_int(width)?,
313 Self::try_into_int(height)?,
314 format as ZXing_ImageFormat,
315 Self::try_into_int(row_stride)?,
316 Self::try_into_int(pix_stride)?,
317 );
318 last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
319 }
320
321 pub fn from_slice<T: TryInto<c_int>>(data: &'a [u8], width: T, height: T, format: ImageFormat) -> Result<Self, Error> {
322 unsafe {
323 let iv = ZXing_ImageView_new_checked(
324 data.as_ptr(),
325 data.len() as c_int,
326 Self::try_into_int(width)?,
327 Self::try_into_int(height)?,
328 format as ZXing_ImageFormat,
329 0,
330 0,
331 );
332 last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
333 }
334 }
335
336 pub fn cropped(self, left: i32, top: i32, width: i32, height: i32) -> Self {
337 unsafe { ZXing_ImageView_crop((self.0).0, left, top, width, height) }
338 self
339 }
340
341 pub fn rotated(self, degree: i32) -> Self {
342 unsafe { ZXing_ImageView_rotate((self.0).0, degree) }
343 self
344 }
345}
346
347#[cfg(feature = "image")]
348use image;
349
350#[cfg(feature = "image")]
351impl<'a> From<&'a image::GrayImage> for ImageView<'a> {
352 fn from(img: &'a image::GrayImage) -> Self {
353 ImageView::from_slice(img.as_ref(), img.width(), img.height(), ImageFormat::Lum).unwrap()
354 }
355}
356
357#[cfg(feature = "image")]
358impl<'a> TryFrom<&'a image::DynamicImage> for ImageView<'a> {
359 type Error = Error;
360
361 fn try_from(img: &'a image::DynamicImage) -> Result<Self, Error> {
362 let format = match img {
363 image::DynamicImage::ImageLuma8(_) => Some(ImageFormat::Lum),
364 image::DynamicImage::ImageLumaA8(_) => Some(ImageFormat::LumA),
365 image::DynamicImage::ImageRgb8(_) => Some(ImageFormat::RGB),
366 image::DynamicImage::ImageRgba8(_) => Some(ImageFormat::RGBA),
367 _ => None,
368 };
369 match format {
370 Some(format) => Ok(ImageView::from_slice(img.as_bytes(), img.width(), img.height(), format)?),
371 None => Err(Error::InvalidInput("Invalid image format (must be either luma8|lumaA8|rgb8|rgba8)".to_string())),
372 }
373 }
374}
375
376make_zxing_class!(Image, ZXing_Image);
377
378impl Image {
379 getter!(Image, width, transmute, i32);
380 getter!(Image, height, transmute, i32);
381 getter!(Image, format, transmute, ImageFormat);
382
383 pub fn data(&self) -> Vec<u8> {
384 let ptr = unsafe { ZXing_Image_data(self.0) };
385 if ptr.is_null() {
386 Vec::<u8>::new()
387 } else {
388 unsafe { std::slice::from_raw_parts(ptr, (self.width() * self.height()) as usize).to_vec() }
389 }
390 }
391}
392
393#[cfg(feature = "image")]
394impl From<&Image> for image::GrayImage {
395 fn from(img: &Image) -> image::GrayImage {
396 image::GrayImage::from_vec(img.width() as u32, img.height() as u32, img.data()).unwrap()
397 }
398}
399
400#[derive(Error, Debug, PartialEq)]
401pub enum BarcodeError {
402 #[error("")]
403 None(),
404
405 #[error("{0}")]
406 Checksum(String),
407
408 #[error("{0}")]
409 Format(String),
410
411 #[error("{0}")]
412 Unsupported(String),
413}
414
415pub type PointI = ZXing_PointI;
416#[repr(C)]
417#[derive(Debug, Copy, Clone)]
418pub struct Position {
419 pub top_left: PointI,
420 pub top_right: PointI,
421 pub bottom_right: PointI,
422 pub bottom_left: PointI,
423}
424
425impl Display for PointI {
426 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
427 write!(f, "{}x{}", self.x, self.y)
428 }
429}
430
431impl Display for Position {
432 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
433 write!(f, "{}", unsafe {
434 c2r_str(ZXing_PositionToString(*(self as *const Position as *const ZXing_Position)))
435 })
436 }
437}
438
439make_zxing_class!(Barcode, ZXing_Barcode);
440
441impl Barcode {
442 getter!(Barcode, isValid, transmute, bool);
443 getter!(Barcode, format, (|f| BarcodeFormats::new(f).unwrap().into_iter().last().unwrap()), BarcodeFormat);
444 getter!(Barcode, contentType, transmute, ContentType);
445 getter!(Barcode, text, c2r_str, String);
446 getter!(Barcode, ecLevel, c2r_str, String);
447 getter!(Barcode, symbologyIdentifier, c2r_str, String);
448 getter!(Barcode, position, transmute, Position);
449 getter!(Barcode, orientation, transmute, i32);
450 getter!(Barcode, hasECI, has_eci, transmute, bool);
451 getter!(Barcode, isInverted, transmute, bool);
452 getter!(Barcode, isMirrored, transmute, bool);
453 getter!(Barcode, lineCount, transmute, i32);
454
455 pub fn bytes(&self) -> Vec<u8> {
456 let mut len: c_int = 0;
457 unsafe { c2r_vec(ZXing_Barcode_bytes(self.0, &mut len), len) }
458 }
459 pub fn bytes_eci(&self) -> Vec<u8> {
460 let mut len: c_int = 0;
461 unsafe { c2r_vec(ZXing_Barcode_bytesECI(self.0, &mut len), len) }
462 }
463
464 pub fn error(&self) -> BarcodeError {
465 let error_type = unsafe { ZXing_Barcode_errorType(self.0) };
466 let error_msg = unsafe { c2r_str(ZXing_Barcode_errorMsg(self.0)) };
467 #[allow(non_upper_case_globals)]
468 match error_type {
469 ZXing_ErrorType_None => BarcodeError::None(),
470 ZXing_ErrorType_Format => BarcodeError::Format(error_msg),
471 ZXing_ErrorType_Checksum => BarcodeError::Checksum(error_msg),
472 ZXing_ErrorType_Unsupported => BarcodeError::Unsupported(error_msg),
473 _ => panic!("Internal error: invalid ZXing_ErrorType"),
474 }
475 }
476
477 pub fn to_svg_with(&self, opts: &BarcodeWriter) -> Result<String, Error> {
478 let str = unsafe { ZXing_WriteBarcodeToSVG(self.0, opts.0) };
479 last_error_if_null_or!(str, c2r_str(str))
480 }
481
482 pub fn to_svg(&self) -> Result<String, Error> {
483 self.to_svg_with(&BarcodeWriter::default())
484 }
485
486 pub fn to_image_with(&self, opts: &BarcodeWriter) -> Result<Image, Error> {
487 let img = unsafe { ZXing_WriteBarcodeToImage(self.0, opts.0) };
488 last_error_if_null_or!(img, Image(img))
489 }
490
491 pub fn to_image(&self) -> Result<Image, Error> {
492 self.to_image_with(&BarcodeWriter::default())
493 }
494}
495
496make_zxing_class_with_default!(BarcodeReader, ZXing_ReaderOptions);
497
498impl BarcodeReader {
499 property!(ReaderOptions, TryHarder, bool);
500 property!(ReaderOptions, TryRotate, bool);
501 property!(ReaderOptions, TryInvert, bool);
502 property!(ReaderOptions, TryDownscale, bool);
503 property!(ReaderOptions, IsPure, bool);
504 property!(ReaderOptions, ReturnErrors, bool);
505 property!(ReaderOptions, Formats, BarcodeFormats);
506 property!(ReaderOptions, TextMode, TextMode);
507 property!(ReaderOptions, Binarizer, Binarizer);
508 property!(ReaderOptions, EanAddOnSymbol, EanAddOnSymbol);
509 property!(ReaderOptions, MaxNumberOfSymbols, i32);
510 property!(ReaderOptions, MinLineCount, i32);
511
512 pub fn from<'a, IV>(&self, image: IV) -> Result<Vec<Barcode>, Error>
513 where
514 IV: TryInto<ImageView<'a>>,
515 IV::Error: Into<Error>,
516 {
517 let iv_: ImageView = image.try_into().map_err(Into::into)?;
518 unsafe {
519 let results = ZXing_ReadBarcodes((iv_.0).0, self.0);
520 if !results.is_null() {
521 let size = ZXing_Barcodes_size(results);
522 let mut vec = Vec::<Barcode>::with_capacity(size as usize);
523 for i in 0..size {
524 vec.push(Barcode(ZXing_Barcodes_move(results, i)));
525 }
526 ZXing_Barcodes_delete(results);
527 Ok(vec)
528 } else {
529 Err(last_error())
530 }
531 }
532 }
533}
534
535make_zxing_class!(BarcodeCreator, ZXing_CreatorOptions);
536
537impl BarcodeCreator {
538 pub fn new(format: BarcodeFormat) -> Self {
539 unsafe { BarcodeCreator(ZXing_CreatorOptions_new(BarcodeFormats::from(format).bits())) }
540 }
541
542 property!(CreatorOptions, ReaderInit, bool);
543 property!(CreatorOptions, ForceSquareDataMatrix, bool);
544 property!(CreatorOptions, EcLevel, String);
545
546 pub fn from_str(&self, str: impl AsRef<str>) -> Result<Barcode, Error> {
547 let cstr = CString::new(str.as_ref())?;
548 let bc = unsafe { ZXing_CreateBarcodeFromText(cstr.as_ptr(), 0, self.0) };
549 last_error_if_null_or!(bc, Barcode(bc))
550 }
551
552 pub fn from_slice(&self, data: impl AsRef<[u8]>) -> Result<Barcode, Error> {
553 let data = data.as_ref();
554 let bc = unsafe { ZXing_CreateBarcodeFromBytes(data.as_ptr() as *const c_void, data.len() as i32, self.0) };
555 last_error_if_null_or!(bc, Barcode(bc))
556 }
557}
558
559make_zxing_class_with_default!(BarcodeWriter, ZXing_WriterOptions);
560
561impl BarcodeWriter {
562 property!(WriterOptions, Scale, i32);
563 property!(WriterOptions, SizeHint, i32);
564 property!(WriterOptions, Rotate, i32);
565 property!(WriterOptions, WithHRT, with_hrt, bool);
566 property!(WriterOptions, WithQuietZones, bool);
567}
568
569pub fn read() -> BarcodeReader {
570 BarcodeReader::default()
571}
572
573pub fn create(format: BarcodeFormat) -> BarcodeCreator {
574 BarcodeCreator::new(format)
575}
576
577pub fn write() -> BarcodeWriter {
578 BarcodeWriter::default()
579}