1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
use std::ffi;
use std::ptr;
use std::slice;
#[link(name = "iec16022")]
unsafe extern "C" {
/// encoding function
///
/// Returns the grid (malloced) containing the matrix. L corner at 0,0.
/// Takes suggested size in `w_ptr`, `h_ptr`, or 0,0. Fills in actual size.
/// Takes `barcodelen` and `barcode` to be encoded
/// Note, if `encodingptr` is null, then fills with auto picked (malloced)
/// encoding.
/// If `lenp` not null, then the length of encoded data before any final unlatch
/// or pad is stored.
/// If `maxp` not null, then the max storage of this size code is stored
/// If `eccp` not null, then the number of ecc bytes used in this size is stored
/// Returns 0 on error (writes to stderr with details).
fn iec16022ecc200f(
w_ptr: *mut ffi::c_int,
h_ptr: *mut ffi::c_int,
encodingptr: *mut *mut ffi::c_char,
barcodelen: ffi::c_int,
barcode: *const ffi::c_uchar,
lenp: *mut ffi::c_int,
maxp: *mut ffi::c_int,
eccp: *mut ffi::c_int,
flags: ffi::c_int,
) -> *mut ffi::c_uchar;
}
#[derive(PartialEq, Eq, Debug)]
pub enum EncodeError {
DataTooLong,
Encoder,
DimensionsTooLarge,
}
#[derive(Debug)]
pub struct DataMatrix {
width: usize,
height: usize,
grid_ptr: *mut ffi::c_uchar,
}
impl DataMatrix {
/// Encodes data as a data matrix
pub fn encode(data: &[u8]) -> Result<Self, EncodeError> {
let mut width = 0;
let mut height = 0;
let data_ptr = data.as_ptr();
let data_len = data
.len()
.try_into()
.map_err(|_| EncodeError::DataTooLong)?;
// SAFETY: ffi
let grid_ptr = unsafe {
iec16022ecc200f(
&raw mut width,
&raw mut height,
ptr::null_mut(),
data_len,
data_ptr,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
)
};
if grid_ptr.is_null() || width == 0 || height == 0 {
Err(EncodeError::Encoder)
} else {
Ok(Self {
width: width
.try_into()
.map_err(|_| EncodeError::DimensionsTooLarge)?,
height: height
.try_into()
.map_err(|_| EncodeError::DimensionsTooLarge)?,
grid_ptr,
})
}
}
#[must_use]
pub const fn width(&self) -> usize {
self.width
}
#[must_use]
pub const fn height(&self) -> usize {
self.height
}
#[must_use]
const fn grid(&self) -> &[bool] {
// SAFETY: returned grid consists only contains values 0 and 1
unsafe { slice::from_raw_parts(self.grid_ptr as *const bool, self.width * self.height) }
}
#[must_use]
pub const fn get(&self, row: usize, col: usize) -> bool {
assert!(row < self.height(), "row index out of bounds");
assert!(col < self.width(), "col index out of bounds");
let idx = row * self.width + col;
self.grid()[idx]
}
#[cfg(feature = "svg")]
pub fn svg(&self) -> svg::Document {
use svg::node::element::Rectangle;
let mut svg = svg::Document::new()
// -1, -1 for quiet zone
// +2 for size of quiet zone
.set("viewBox", format!("-1 -1 {} {}", self.width() + 2, self.height() + 2));
for row in 0..self.height() {
for col in 0..self.width() {
if self.get(row, col) {
svg = svg.add(
Rectangle::new()
.set("width", "1")
.set("height", "1")
.set("x", col)
.set("y", self.height() - row - 1),
);
}
}
}
svg
}
}
impl Drop for DataMatrix {
fn drop(&mut self) {
// SAFETY: self.ptr malloc'ed in ffi
unsafe { libc::free(self.grid_ptr.cast()) };
}
}
|