zbar_pack/
lib.rs

1//! Safe Rust bindings for ZBar barcode scanner library
2//!
3//! This crate provides a safe Rust wrapper around the ZBar C library,
4//! with static linking of vendored ZBar source code by default.
5//!
6//! # Features
7//!
8//! - `vendored` (default): Compile and statically link bundled ZBar source
9//! - `system`: Use system-installed ZBar library
10//! - `dynamic`: Dynamically link system ZBar library
11//! - `all-codecs` (default): Enable all codecs
12//! - `minimal`: Minimal build, manually enable required codecs
13//!
14//! # License Compliance
15//!
16//! ZBar is licensed under LGPLv2.1+. When statically linking, this project must:
17//! - Provide complete ZBar source code and build instructions (see zbar-src/)
18//! - Allow end users to replace/relink the library
19//! - See COMPLIANCE.md for details
20//!
21//! # Example
22//!
23//! ```no_run
24//! use zbar_pack::{ImageScanner, Image, SymbolType};
25//!
26//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
27//! let mut scanner = ImageScanner::new()?;
28//! scanner.set_config(SymbolType::QRCODE, 0, 1)?;
29//!
30//! // Example: 100x100 grayscale image
31//! let data: Vec<u8> = vec![0; 100 * 100];
32//! let image = Image::from_gray(&data, 100, 100)?;
33//! let symbols = scanner.scan_image(&image)?;
34//!
35//! for symbol in symbols {
36//!     println!("Type: {:?}, Data: {}", symbol.symbol_type(), symbol.data());
37//! }
38//! # Ok(())
39//! # }
40//! ```
41
42use std::fmt;
43use std::marker::PhantomData;
44use std::os::raw::c_int;
45use std::ptr::NonNull;
46
47use zbar_sys as ffi;
48
49/// ZBar error types
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum Error {
52    /// Invalid argument
53    InvalidArgument,
54    /// Out of memory
55    OutOfMemory,
56    /// Internal error
57    InternalError,
58    /// System error
59    SystemError,
60    /// Unsupported operation
61    Unsupported,
62    /// Unknown error
63    Unknown(i32),
64}
65
66impl fmt::Display for Error {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            Error::InvalidArgument => write!(f, "invalid argument"),
70            Error::OutOfMemory => write!(f, "out of memory"),
71            Error::InternalError => write!(f, "internal error"),
72            Error::SystemError => write!(f, "system error"),
73            Error::Unsupported => write!(f, "unsupported operation"),
74            Error::Unknown(code) => write!(f, "unknown error: {}", code),
75        }
76    }
77}
78
79impl std::error::Error for Error {}
80
81pub type Result<T> = std::result::Result<T, Error>;
82
83/// Symbol types (barcode/QR code types)
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85#[repr(u32)]
86pub enum SymbolType {
87    None = ffi::ZBAR_NONE,
88    Partial = ffi::ZBAR_PARTIAL,
89    EAN2 = ffi::ZBAR_EAN2,
90    EAN5 = ffi::ZBAR_EAN5,
91    EAN8 = ffi::ZBAR_EAN8,
92    UPCE = ffi::ZBAR_UPCE,
93    ISBN10 = ffi::ZBAR_ISBN10,
94    UPCA = ffi::ZBAR_UPCA,
95    EAN13 = ffi::ZBAR_EAN13,
96    ISBN13 = ffi::ZBAR_ISBN13,
97    COMPOSITE = ffi::ZBAR_COMPOSITE,
98    I25 = ffi::ZBAR_I25,
99    DATABAR = ffi::ZBAR_DATABAR,
100    DatabarExp = ffi::ZBAR_DATABAR_EXP,
101    CODABAR = ffi::ZBAR_CODABAR,
102    CODE39 = ffi::ZBAR_CODE39,
103    PDF417 = ffi::ZBAR_PDF417,
104    QRCODE = ffi::ZBAR_QRCODE,
105    SQCODE = ffi::ZBAR_SQCODE,
106    CODE93 = ffi::ZBAR_CODE93,
107    CODE128 = ffi::ZBAR_CODE128,
108}
109
110/// Image object
111pub struct Image {
112    raw: NonNull<ffi::zbar_image_t>,
113    owned: bool,
114}
115
116impl Image {
117    /// Create from grayscale image data
118    pub fn from_gray(data: &[u8], width: u32, height: u32) -> Result<Self> {
119        unsafe {
120            let img = ffi::zbar_image_create();
121            if img.is_null() {
122                return Err(Error::OutOfMemory);
123            }
124
125            // Y800 format (grayscale): fourcc('Y', '8', '0', '0')
126            let format =
127                ('Y' as u64) | (('8' as u64) << 8) | (('0' as u64) << 16) | (('0' as u64) << 24);
128            ffi::zbar_image_set_format(img, format);
129            ffi::zbar_image_set_size(img, width, height);
130            ffi::zbar_image_set_data(
131                img,
132                data.as_ptr() as *const _,
133                (width * height) as u64,
134                None,
135            );
136
137            Ok(Image {
138                raw: NonNull::new_unchecked(img),
139                owned: true,
140            })
141        }
142    }
143
144    /// Get raw pointer
145    pub(crate) fn as_ptr(&self) -> *mut ffi::zbar_image_t {
146        self.raw.as_ptr()
147    }
148}
149
150impl Drop for Image {
151    fn drop(&mut self) {
152        if self.owned {
153            unsafe {
154                ffi::zbar_image_destroy(self.raw.as_ptr());
155            }
156        }
157    }
158}
159
160unsafe impl Send for Image {}
161unsafe impl Sync for Image {}
162
163/// Scanned symbol
164#[derive(Clone)]
165pub struct Symbol<'a> {
166    raw: *const ffi::zbar_symbol_t,
167    _phantom: PhantomData<&'a ()>,
168}
169
170impl<'a> Symbol<'a> {
171    /// Get symbol type
172    pub fn symbol_type(&self) -> SymbolType {
173        unsafe {
174            let t = ffi::zbar_symbol_get_type(self.raw);
175            std::mem::transmute(t)
176        }
177    }
178
179    /// Get symbol data
180    pub fn data(&self) -> &str {
181        unsafe {
182            let ptr = ffi::zbar_symbol_get_data(self.raw);
183            let len = ffi::zbar_symbol_get_data_length(self.raw) as usize;
184            let slice = std::slice::from_raw_parts(ptr as *const u8, len);
185            std::str::from_utf8_unchecked(slice)
186        }
187    }
188
189    /// Get symbol quality
190    pub fn quality(&self) -> i32 {
191        unsafe { ffi::zbar_symbol_get_quality(self.raw) }
192    }
193}
194
195/// Symbol iterator
196pub struct SymbolIter<'a> {
197    current: *const ffi::zbar_symbol_t,
198    _phantom: PhantomData<&'a ()>,
199}
200
201impl<'a> Iterator for SymbolIter<'a> {
202    type Item = Symbol<'a>;
203
204    fn next(&mut self) -> Option<Self::Item> {
205        if self.current.is_null() {
206            return None;
207        }
208
209        let symbol = Symbol {
210            raw: self.current,
211            _phantom: PhantomData,
212        };
213
214        unsafe {
215            self.current = ffi::zbar_symbol_next(self.current);
216        }
217
218        Some(symbol)
219    }
220}
221
222/// Image scanner
223pub struct ImageScanner {
224    raw: NonNull<ffi::zbar_image_scanner_t>,
225}
226
227impl ImageScanner {
228    /// Create new image scanner
229    pub fn new() -> Result<Self> {
230        unsafe {
231            let scanner = ffi::zbar_image_scanner_create();
232            if scanner.is_null() {
233                return Err(Error::OutOfMemory);
234            }
235
236            Ok(ImageScanner {
237                raw: NonNull::new_unchecked(scanner),
238            })
239        }
240    }
241
242    /// Set configuration
243    pub fn set_config(&mut self, symbol_type: SymbolType, config: u32, value: c_int) -> Result<()> {
244        unsafe {
245            let ret = ffi::zbar_image_scanner_set_config(
246                self.raw.as_ptr(),
247                symbol_type as u32,
248                config,
249                value,
250            );
251            if ret != 0 {
252                return Err(Error::InvalidArgument);
253            }
254            Ok(())
255        }
256    }
257
258    /// Scan image
259    pub fn scan_image<'a>(&mut self, image: &'a Image) -> Result<SymbolIter<'a>> {
260        unsafe {
261            let n = ffi::zbar_scan_image(self.raw.as_ptr(), image.as_ptr());
262            if n < 0 {
263                return Err(Error::InternalError);
264            }
265
266            let first_symbol = ffi::zbar_image_first_symbol(image.as_ptr());
267
268            Ok(SymbolIter {
269                current: first_symbol,
270                _phantom: PhantomData,
271            })
272        }
273    }
274}
275
276impl Default for ImageScanner {
277    fn default() -> Self {
278        Self::new().expect("failed to create image scanner")
279    }
280}
281
282impl Drop for ImageScanner {
283    fn drop(&mut self) {
284        unsafe {
285            ffi::zbar_image_scanner_destroy(self.raw.as_ptr());
286        }
287    }
288}
289
290unsafe impl Send for ImageScanner {}
291unsafe impl Sync for ImageScanner {}
292
293/// Get ZBar version information
294pub fn version() -> (u32, u32) {
295    unsafe {
296        let mut major = 0u32;
297        let mut minor = 0u32;
298        ffi::zbar_version(&mut major, &mut minor, std::ptr::null_mut());
299        (major, minor)
300    }
301}
302
303/// Set global library debug/verbosity level
304///
305/// Controls the amount of debug output from ZBar library.
306///
307/// # Arguments
308///
309/// * `verbosity` - Debug level (0 = silent, higher values = more output)
310///
311/// # Example
312///
313/// ```
314/// use zbar_pack::set_verbosity;
315///
316/// // Disable all debug output
317/// set_verbosity(0);
318/// ```
319pub fn set_verbosity(verbosity: i32) {
320    unsafe {
321        ffi::zbar_set_verbosity(verbosity);
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328
329    #[test]
330    fn test_version() {
331        let (major, minor) = version();
332        assert_eq!(major, 0, "ZBar major version should be 0");
333        assert_eq!(minor, 23, "ZBar minor version should be 23");
334        println!("ZBar version: {}.{}", major, minor);
335    }
336
337    #[test]
338    fn test_scanner_creation() {
339        let scanner = ImageScanner::new();
340        assert!(scanner.is_ok(), "Failed to create scanner");
341    }
342}