diff mupdf-source/thirdparty/zxing-cpp/wrappers/rust/src/lib.rs @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/zxing-cpp/wrappers/rust/src/lib.rs	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,579 @@
+/*
+* Copyright 2024 Axel Waggershauser
+*/
+// SPDX-License-Identifier: Apache-2.0
+
+#![allow(unknown_lints)] // backward compatibility
+#![allow(unused_unsafe)]
+#![allow(clippy::useless_transmute)]
+#![allow(clippy::redundant_closure_call)]
+#![allow(clippy::missing_transmute_annotations)] // introduced in 1.79
+
+mod tests;
+
+#[allow(dead_code)]
+#[allow(non_camel_case_types)]
+#[allow(non_snake_case)]
+#[allow(non_upper_case_globals)]
+mod bindings {
+	include!("bindings.rs");
+}
+
+use bindings::*;
+
+use flagset::{flags, FlagSet};
+use paste::paste;
+use std::ffi::{c_char, c_int, c_uint, c_void, CStr, CString, NulError};
+use std::fmt::{Display, Formatter};
+use std::marker::PhantomData;
+use std::mem::transmute;
+use std::rc::Rc;
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum Error {
+	#[error("{0}")]
+	InvalidInput(String),
+
+	#[error("NulError from CString::new")]
+	NulError(#[from] NulError),
+	//
+	// #[error("data store disconnected")]
+	// IOError(#[from] std::io::Error),
+	// #[error("the data for key `{0}` is not available")]
+	// Redaction(String),
+	// #[error("invalid header (expected {expected:?}, found {found:?})")]
+	// InvalidHeader {
+	//     expected: String,
+	//     found: String,
+	// },
+	// #[error("unknown data store error")]
+	// Unknown,
+}
+
+// see https://github.com/dtolnay/thiserror/issues/62
+impl From<std::convert::Infallible> for Error {
+	fn from(_: std::convert::Infallible) -> Self {
+		unreachable!()
+	}
+}
+
+fn c2r_str(str: *mut c_char) -> String {
+	let mut res = String::new();
+	if !str.is_null() {
+		unsafe { res = CStr::from_ptr(str).to_string_lossy().to_string() };
+		unsafe { ZXing_free(str as *mut c_void) };
+	}
+	res
+}
+
+fn c2r_vec(buf: *mut u8, len: c_int) -> Vec<u8> {
+	let mut res = Vec::<u8>::new();
+	if !buf.is_null() && len > 0 {
+		unsafe { res = std::slice::from_raw_parts(buf, len as usize).to_vec() };
+		unsafe { ZXing_free(buf as *mut c_void) };
+	}
+	res
+}
+
+fn last_error() -> Error {
+	match unsafe { ZXing_LastErrorMsg().as_mut() } {
+		None => panic!("Internal error: ZXing_LastErrorMsg() returned NULL"),
+		Some(error) => Error::InvalidInput(c2r_str(error)),
+	}
+}
+
+macro_rules! last_error_or {
+	($expr:expr) => {
+		match unsafe { ZXing_LastErrorMsg().as_mut() } {
+			None => Ok($expr),
+			Some(error) => Err(Error::InvalidInput(c2r_str(error))),
+		}
+	};
+}
+
+macro_rules! last_error_if_null_or {
+	($ptr:ident, $expr:expr) => {
+		match $ptr.is_null() {
+			true => Err(last_error()),
+			false => Ok($expr),
+		}
+	};
+}
+
+macro_rules! make_zxing_class {
+	($r_class:ident, $c_class:ident) => {
+		paste! {
+			pub struct $r_class(*mut $c_class);
+
+			impl Drop for $r_class {
+				fn drop(&mut self) {
+					unsafe { [<$c_class _delete>](self.0) }
+				}
+			}
+		}
+	};
+}
+
+macro_rules! make_zxing_class_with_default {
+	($r_class:ident, $c_class:ident) => {
+		make_zxing_class!($r_class, $c_class);
+		paste! {
+			impl $r_class {
+				pub fn new() -> Self {
+					unsafe { $r_class([<$c_class _new>]()) }
+				}
+			}
+
+			impl Default for $r_class {
+				fn default() -> Self {
+					Self::new()
+				}
+			}
+		}
+	};
+}
+
+macro_rules! getter {
+	($class:ident, $c_name:ident, $r_name:ident, $conv:expr, $type:ty) => {
+		pub fn $r_name(&self) -> $type {
+			paste! { unsafe { $conv([<ZXing_ $class _ $c_name>](self.0)) } }
+		}
+	};
+	($class:ident, $c_name:ident, $conv:expr, $type:ty) => {
+		paste! { getter! { $class, $c_name, [<$c_name:snake>], $conv, $type } }
+	};
+}
+
+macro_rules! property {
+	($class:ident, $c_name:ident, $r_name:ident, String) => {
+		pub fn $r_name(self, v: impl AsRef<str>) -> Self {
+			let cstr = CString::new(v.as_ref()).unwrap();
+			paste! { unsafe { [<ZXing_ $class _set $c_name>](self.0, cstr.as_ptr()) } };
+			self
+		}
+
+		paste! {
+			pub fn [<set_ $r_name>](&mut self, v : impl AsRef<str>) -> &mut Self {
+				let cstr = CString::new(v.as_ref()).unwrap();
+				unsafe { [<ZXing_ $class _set $c_name>](self.0, cstr.as_ptr()) };
+				self
+			}
+
+			pub fn [<get_ $r_name>](&self) -> String {
+				unsafe { c2r_str([<ZXing_ $class _get $c_name>](self.0)) }
+			}
+		}
+	};
+
+	($class:ident, $c_name:ident, $r_name:ident, $type:ty) => {
+		pub fn $r_name(self, v: impl Into<$type>) -> Self {
+			paste! { unsafe { [<ZXing_ $class _set $c_name>](self.0, transmute(v.into())) } };
+			self
+		}
+
+		paste! {
+			pub fn [<set_ $r_name>](&mut self, v : impl Into<$type>) -> &mut Self {
+				unsafe { [<ZXing_ $class _set $c_name>](self.0, transmute(v.into())) };
+				self
+			}
+
+			pub fn [<get_ $r_name>](&self) -> $type {
+				unsafe { transmute([<ZXing_ $class _get $c_name>](self.0)) }
+			}
+		}
+	};
+
+	($class:ident, $c_name:ident, $type:ty) => {
+		paste! { property! { $class, $c_name, [<$c_name:snake>], $type } }
+	};
+}
+
+macro_rules! make_zxing_enum {
+    ($name:ident { $($field:ident),* }) => {
+        #[repr(u32)]
+        #[derive(Debug, Copy, Clone, PartialEq)]
+        pub enum $name {
+            $($field = paste! { [<ZXing_ $name _ $field>] },)*
+        }
+    }
+}
+
+macro_rules! make_zxing_flags {
+    ($name:ident { $($field:ident),* }) => {
+        flags! {
+            #[repr(u32)]
+            pub enum $name: c_uint {
+                $($field = paste! { [<ZXing_ $name _ $field>] },)*
+            }
+        }
+    }
+}
+#[rustfmt::skip] // workaround for broken #[rustfmt::skip::macros(make_zxing_enum)]
+make_zxing_enum!(ImageFormat { Lum, LumA, RGB, BGR, RGBA, ARGB, BGRA, ABGR });
+#[rustfmt::skip]
+make_zxing_enum!(ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI });
+#[rustfmt::skip]
+make_zxing_enum!(Binarizer { LocalAverage, GlobalHistogram, FixedThreshold, BoolCast });
+#[rustfmt::skip]
+make_zxing_enum!(TextMode { Plain, ECI, HRI, Hex, Escaped });
+#[rustfmt::skip]
+make_zxing_enum!(EanAddOnSymbol { Ignore, Read, Require });
+
+#[rustfmt::skip]
+make_zxing_flags!(BarcodeFormat {
+	None, Aztec, Codabar, Code39, Code93, Code128, DataBar, DataBarExpanded, DataBarLimited, DataMatrix,
+	EAN8, EAN13, ITF, MaxiCode, PDF417, QRCode, UPCA, UPCE, MicroQRCode, RMQRCode, DXFilmEdge,
+	LinearCodes, MatrixCodes, Any
+});
+
+impl Display for BarcodeFormat {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		write!(f, "{}", unsafe { c2r_str(ZXing_BarcodeFormatToString(BarcodeFormats::from(*self).bits())) })
+	}
+}
+
+impl Display for ContentType {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		write!(f, "{}", unsafe { c2r_str(ZXing_ContentTypeToString(transmute(*self))) })
+	}
+}
+
+pub type BarcodeFormats = FlagSet<BarcodeFormat>;
+
+pub trait FromStr: Sized {
+	fn from_str(str: impl AsRef<str>) -> Result<Self, Error>;
+}
+
+impl FromStr for BarcodeFormat {
+	fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormat, Error> {
+		let cstr = CString::new(str.as_ref())?;
+		let res = unsafe { BarcodeFormats::new_unchecked(ZXing_BarcodeFormatFromString(cstr.as_ptr())) };
+		match res.bits() {
+			u32::MAX => last_error_or!(BarcodeFormat::None),
+			_ => Ok(res.into_iter().last().unwrap()),
+		}
+	}
+}
+
+impl FromStr for BarcodeFormats {
+	fn from_str(str: impl AsRef<str>) -> Result<BarcodeFormats, Error> {
+		let cstr = CString::new(str.as_ref())?;
+		let res = unsafe { BarcodeFormats::new_unchecked(ZXing_BarcodeFormatsFromString(cstr.as_ptr())) };
+		match res.bits() {
+			u32::MAX => last_error_or!(BarcodeFormats::default()),
+			0 => Ok(BarcodeFormats::full()),
+			_ => Ok(res),
+		}
+	}
+}
+
+#[derive(Debug, PartialEq)]
+struct ImageViewOwner<'a>(*mut ZXing_ImageView, PhantomData<&'a u8>);
+
+impl Drop for ImageViewOwner<'_> {
+	fn drop(&mut self) {
+		unsafe { ZXing_ImageView_delete(self.0) }
+	}
+}
+#[derive(Debug, Clone, PartialEq)]
+pub struct ImageView<'a>(Rc<ImageViewOwner<'a>>);
+
+impl<'a> From<&'a ImageView<'a>> for ImageView<'a> {
+	fn from(img: &'a ImageView) -> Self {
+		img.clone()
+	}
+}
+
+impl<'a> ImageView<'a> {
+	fn try_into_int<T: TryInto<c_int>>(val: T) -> Result<c_int, Error> {
+		val.try_into().map_err(|_| Error::InvalidInput("Could not convert Integer into c_int.".to_string()))
+	}
+
+	/// Constructs an ImageView from a raw pointer and the width/height (in pixels)
+	/// and row_stride/pix_stride (in bytes).
+	///
+	/// # Safety
+	///
+	/// The memory gets accessed inside the c++ library at random places between
+	/// `ptr` and `ptr + height * row_stride` or `ptr + width * pix_stride`.
+	/// Note that both the stride values could be negative, e.g. if the image
+	/// view is rotated.
+	pub unsafe fn from_ptr<T: TryInto<c_int>, U: TryInto<c_int>>(
+		ptr: *const u8,
+		width: T,
+		height: T,
+		format: ImageFormat,
+		row_stride: U,
+		pix_stride: U,
+	) -> Result<Self, Error> {
+		let iv = ZXing_ImageView_new(
+			ptr,
+			Self::try_into_int(width)?,
+			Self::try_into_int(height)?,
+			format as ZXing_ImageFormat,
+			Self::try_into_int(row_stride)?,
+			Self::try_into_int(pix_stride)?,
+		);
+		last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
+	}
+
+	pub fn from_slice<T: TryInto<c_int>>(data: &'a [u8], width: T, height: T, format: ImageFormat) -> Result<Self, Error> {
+		unsafe {
+			let iv = ZXing_ImageView_new_checked(
+				data.as_ptr(),
+				data.len() as c_int,
+				Self::try_into_int(width)?,
+				Self::try_into_int(height)?,
+				format as ZXing_ImageFormat,
+				0,
+				0,
+			);
+			last_error_if_null_or!(iv, ImageView(Rc::new(ImageViewOwner(iv, PhantomData))))
+		}
+	}
+
+	pub fn cropped(self, left: i32, top: i32, width: i32, height: i32) -> Self {
+		unsafe { ZXing_ImageView_crop((self.0).0, left, top, width, height) }
+		self
+	}
+
+	pub fn rotated(self, degree: i32) -> Self {
+		unsafe { ZXing_ImageView_rotate((self.0).0, degree) }
+		self
+	}
+}
+
+#[cfg(feature = "image")]
+use image;
+
+#[cfg(feature = "image")]
+impl<'a> From<&'a image::GrayImage> for ImageView<'a> {
+	fn from(img: &'a image::GrayImage) -> Self {
+		ImageView::from_slice(img.as_ref(), img.width(), img.height(), ImageFormat::Lum).unwrap()
+	}
+}
+
+#[cfg(feature = "image")]
+impl<'a> TryFrom<&'a image::DynamicImage> for ImageView<'a> {
+	type Error = Error;
+
+	fn try_from(img: &'a image::DynamicImage) -> Result<Self, Error> {
+		let format = match img {
+			image::DynamicImage::ImageLuma8(_) => Some(ImageFormat::Lum),
+			image::DynamicImage::ImageLumaA8(_) => Some(ImageFormat::LumA),
+			image::DynamicImage::ImageRgb8(_) => Some(ImageFormat::RGB),
+			image::DynamicImage::ImageRgba8(_) => Some(ImageFormat::RGBA),
+			_ => None,
+		};
+		match format {
+			Some(format) => Ok(ImageView::from_slice(img.as_bytes(), img.width(), img.height(), format)?),
+			None => Err(Error::InvalidInput("Invalid image format (must be either luma8|lumaA8|rgb8|rgba8)".to_string())),
+		}
+	}
+}
+
+make_zxing_class!(Image, ZXing_Image);
+
+impl Image {
+	getter!(Image, width, transmute, i32);
+	getter!(Image, height, transmute, i32);
+	getter!(Image, format, transmute, ImageFormat);
+
+	pub fn data(&self) -> Vec<u8> {
+		let ptr = unsafe { ZXing_Image_data(self.0) };
+		if ptr.is_null() {
+			Vec::<u8>::new()
+		} else {
+			unsafe { std::slice::from_raw_parts(ptr, (self.width() * self.height()) as usize).to_vec() }
+		}
+	}
+}
+
+#[cfg(feature = "image")]
+impl From<&Image> for image::GrayImage {
+	fn from(img: &Image) -> image::GrayImage {
+		image::GrayImage::from_vec(img.width() as u32, img.height() as u32, img.data()).unwrap()
+	}
+}
+
+#[derive(Error, Debug, PartialEq)]
+pub enum BarcodeError {
+	#[error("")]
+	None(),
+
+	#[error("{0}")]
+	Checksum(String),
+
+	#[error("{0}")]
+	Format(String),
+
+	#[error("{0}")]
+	Unsupported(String),
+}
+
+pub type PointI = ZXing_PointI;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Position {
+	pub top_left: PointI,
+	pub top_right: PointI,
+	pub bottom_right: PointI,
+	pub bottom_left: PointI,
+}
+
+impl Display for PointI {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		write!(f, "{}x{}", self.x, self.y)
+	}
+}
+
+impl Display for Position {
+	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+		write!(f, "{}", unsafe {
+			c2r_str(ZXing_PositionToString(*(self as *const Position as *const ZXing_Position)))
+		})
+	}
+}
+
+make_zxing_class!(Barcode, ZXing_Barcode);
+
+impl Barcode {
+	getter!(Barcode, isValid, transmute, bool);
+	getter!(Barcode, format, (|f| BarcodeFormats::new(f).unwrap().into_iter().last().unwrap()), BarcodeFormat);
+	getter!(Barcode, contentType, transmute, ContentType);
+	getter!(Barcode, text, c2r_str, String);
+	getter!(Barcode, ecLevel, c2r_str, String);
+	getter!(Barcode, symbologyIdentifier, c2r_str, String);
+	getter!(Barcode, position, transmute, Position);
+	getter!(Barcode, orientation, transmute, i32);
+	getter!(Barcode, hasECI, has_eci, transmute, bool);
+	getter!(Barcode, isInverted, transmute, bool);
+	getter!(Barcode, isMirrored, transmute, bool);
+	getter!(Barcode, lineCount, transmute, i32);
+
+	pub fn bytes(&self) -> Vec<u8> {
+		let mut len: c_int = 0;
+		unsafe { c2r_vec(ZXing_Barcode_bytes(self.0, &mut len), len) }
+	}
+	pub fn bytes_eci(&self) -> Vec<u8> {
+		let mut len: c_int = 0;
+		unsafe { c2r_vec(ZXing_Barcode_bytesECI(self.0, &mut len), len) }
+	}
+
+	pub fn error(&self) -> BarcodeError {
+		let error_type = unsafe { ZXing_Barcode_errorType(self.0) };
+		let error_msg = unsafe { c2r_str(ZXing_Barcode_errorMsg(self.0)) };
+		#[allow(non_upper_case_globals)]
+		match error_type {
+			ZXing_ErrorType_None => BarcodeError::None(),
+			ZXing_ErrorType_Format => BarcodeError::Format(error_msg),
+			ZXing_ErrorType_Checksum => BarcodeError::Checksum(error_msg),
+			ZXing_ErrorType_Unsupported => BarcodeError::Unsupported(error_msg),
+			_ => panic!("Internal error: invalid ZXing_ErrorType"),
+		}
+	}
+
+	pub fn to_svg_with(&self, opts: &BarcodeWriter) -> Result<String, Error> {
+		let str = unsafe { ZXing_WriteBarcodeToSVG(self.0, opts.0) };
+		last_error_if_null_or!(str, c2r_str(str))
+	}
+
+	pub fn to_svg(&self) -> Result<String, Error> {
+		self.to_svg_with(&BarcodeWriter::default())
+	}
+
+	pub fn to_image_with(&self, opts: &BarcodeWriter) -> Result<Image, Error> {
+		let img = unsafe { ZXing_WriteBarcodeToImage(self.0, opts.0) };
+		last_error_if_null_or!(img, Image(img))
+	}
+
+	pub fn to_image(&self) -> Result<Image, Error> {
+		self.to_image_with(&BarcodeWriter::default())
+	}
+}
+
+make_zxing_class_with_default!(BarcodeReader, ZXing_ReaderOptions);
+
+impl BarcodeReader {
+	property!(ReaderOptions, TryHarder, bool);
+	property!(ReaderOptions, TryRotate, bool);
+	property!(ReaderOptions, TryInvert, bool);
+	property!(ReaderOptions, TryDownscale, bool);
+	property!(ReaderOptions, IsPure, bool);
+	property!(ReaderOptions, ReturnErrors, bool);
+	property!(ReaderOptions, Formats, BarcodeFormats);
+	property!(ReaderOptions, TextMode, TextMode);
+	property!(ReaderOptions, Binarizer, Binarizer);
+	property!(ReaderOptions, EanAddOnSymbol, EanAddOnSymbol);
+	property!(ReaderOptions, MaxNumberOfSymbols, i32);
+	property!(ReaderOptions, MinLineCount, i32);
+
+	pub fn from<'a, IV>(&self, image: IV) -> Result<Vec<Barcode>, Error>
+	where
+		IV: TryInto<ImageView<'a>>,
+		IV::Error: Into<Error>,
+	{
+		let iv_: ImageView = image.try_into().map_err(Into::into)?;
+		unsafe {
+			let results = ZXing_ReadBarcodes((iv_.0).0, self.0);
+			if !results.is_null() {
+				let size = ZXing_Barcodes_size(results);
+				let mut vec = Vec::<Barcode>::with_capacity(size as usize);
+				for i in 0..size {
+					vec.push(Barcode(ZXing_Barcodes_move(results, i)));
+				}
+				ZXing_Barcodes_delete(results);
+				Ok(vec)
+			} else {
+				Err(last_error())
+			}
+		}
+	}
+}
+
+make_zxing_class!(BarcodeCreator, ZXing_CreatorOptions);
+
+impl BarcodeCreator {
+	pub fn new(format: BarcodeFormat) -> Self {
+		unsafe { BarcodeCreator(ZXing_CreatorOptions_new(BarcodeFormats::from(format).bits())) }
+	}
+
+	property!(CreatorOptions, ReaderInit, bool);
+	property!(CreatorOptions, ForceSquareDataMatrix, bool);
+	property!(CreatorOptions, EcLevel, String);
+
+	pub fn from_str(&self, str: impl AsRef<str>) -> Result<Barcode, Error> {
+		let cstr = CString::new(str.as_ref())?;
+		let bc = unsafe { ZXing_CreateBarcodeFromText(cstr.as_ptr(), 0, self.0) };
+		last_error_if_null_or!(bc, Barcode(bc))
+	}
+
+	pub fn from_slice(&self, data: impl AsRef<[u8]>) -> Result<Barcode, Error> {
+		let data = data.as_ref();
+		let bc = unsafe { ZXing_CreateBarcodeFromBytes(data.as_ptr() as *const c_void, data.len() as i32, self.0) };
+		last_error_if_null_or!(bc, Barcode(bc))
+	}
+}
+
+make_zxing_class_with_default!(BarcodeWriter, ZXing_WriterOptions);
+
+impl BarcodeWriter {
+	property!(WriterOptions, Scale, i32);
+	property!(WriterOptions, SizeHint, i32);
+	property!(WriterOptions, Rotate, i32);
+	property!(WriterOptions, WithHRT, with_hrt, bool);
+	property!(WriterOptions, WithQuietZones, bool);
+}
+
+pub fn read() -> BarcodeReader {
+	BarcodeReader::default()
+}
+
+pub fn create(format: BarcodeFormat) -> BarcodeCreator {
+	BarcodeCreator::new(format)
+}
+
+pub fn write() -> BarcodeWriter {
+	BarcodeWriter::default()
+}