imprimatur 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Cargo.lock +33 -3
- data/Cargo.toml +1 -1
- data/README.md +41 -1
- data/ext/imprimatur_native/cli/Cargo.toml +3 -1
- data/ext/imprimatur_native/core/Cargo.toml +15 -4
- data/ext/imprimatur_native/core/README.md +27 -4
- data/ext/imprimatur_native/core/src/barcode/code128.rs +162 -0
- data/ext/imprimatur_native/core/src/barcode/draw.rs +124 -0
- data/ext/imprimatur_native/core/src/barcode/ean.rs +91 -0
- data/ext/imprimatur_native/core/src/barcode/gs1.rs +188 -0
- data/ext/imprimatur_native/core/src/barcode/matrix.rs +130 -0
- data/ext/imprimatur_native/core/src/barcode.rs +314 -0
- data/ext/imprimatur_native/core/src/doc/symbol.rs +136 -0
- data/ext/imprimatur_native/core/src/doc.rs +11 -1
- data/ext/imprimatur_native/core/src/docx.rs +22 -10
- data/ext/imprimatur_native/core/src/error.rs +6 -0
- data/ext/imprimatur_native/core/src/font.rs +3 -1
- data/ext/imprimatur_native/core/src/lib.rs +3 -1
- data/ext/imprimatur_native/core/src/pdf/layout.rs +48 -14
- data/ext/imprimatur_native/core/src/pdf/truetype/descriptor.rs +129 -0
- data/ext/imprimatur_native/core/src/pdf/truetype/program.rs +70 -0
- data/ext/imprimatur_native/core/src/pdf/truetype.rs +149 -0
- data/ext/imprimatur_native/core/src/pdf.rs +9 -1
- data/ext/imprimatur_native/core/src/read/image.rs +17 -0
- data/ext/imprimatur_native/core/src/read.rs +3 -2
- data/ext/imprimatur_native/core/src/style.rs +11 -0
- data/ext/imprimatur_native/ffi/Cargo.toml +1 -1
- data/ext/imprimatur_native/ffi/src/barcode.rs +147 -0
- data/ext/imprimatur_native/ffi/src/document.rs +29 -1
- data/ext/imprimatur_native/ffi/src/lib.rs +5 -3
- data/lib/imprimatur/barcode.rb +78 -0
- data/lib/imprimatur/document.rb +17 -1
- data/lib/imprimatur/version.rb +1 -1
- data/lib/imprimatur.rb +4 -0
- metadata +13 -2
- data/ext/imprimatur_native/core/src/test_support.rs +0 -251
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: imprimatur
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -52,9 +52,16 @@ files:
|
|
|
52
52
|
- ext/imprimatur_native/core/LICENSE
|
|
53
53
|
- ext/imprimatur_native/core/README.md
|
|
54
54
|
- ext/imprimatur_native/core/src/archive.rs
|
|
55
|
+
- ext/imprimatur_native/core/src/barcode.rs
|
|
56
|
+
- ext/imprimatur_native/core/src/barcode/code128.rs
|
|
57
|
+
- ext/imprimatur_native/core/src/barcode/draw.rs
|
|
58
|
+
- ext/imprimatur_native/core/src/barcode/ean.rs
|
|
59
|
+
- ext/imprimatur_native/core/src/barcode/gs1.rs
|
|
60
|
+
- ext/imprimatur_native/core/src/barcode/matrix.rs
|
|
55
61
|
- ext/imprimatur_native/core/src/doc.rs
|
|
56
62
|
- ext/imprimatur_native/core/src/doc/paragraph.rs
|
|
57
63
|
- ext/imprimatur_native/core/src/doc/picture.rs
|
|
64
|
+
- ext/imprimatur_native/core/src/doc/symbol.rs
|
|
58
65
|
- ext/imprimatur_native/core/src/doc/table.rs
|
|
59
66
|
- ext/imprimatur_native/core/src/docx.rs
|
|
60
67
|
- ext/imprimatur_native/core/src/docx/picture.rs
|
|
@@ -69,6 +76,9 @@ files:
|
|
|
69
76
|
- ext/imprimatur_native/core/src/pdf/metrics.rs
|
|
70
77
|
- ext/imprimatur_native/core/src/pdf/objects.rs
|
|
71
78
|
- ext/imprimatur_native/core/src/pdf/picture.rs
|
|
79
|
+
- ext/imprimatur_native/core/src/pdf/truetype.rs
|
|
80
|
+
- ext/imprimatur_native/core/src/pdf/truetype/descriptor.rs
|
|
81
|
+
- ext/imprimatur_native/core/src/pdf/truetype/program.rs
|
|
72
82
|
- ext/imprimatur_native/core/src/read.rs
|
|
73
83
|
- ext/imprimatur_native/core/src/read/budget.rs
|
|
74
84
|
- ext/imprimatur_native/core/src/read/content.rs
|
|
@@ -108,7 +118,6 @@ files:
|
|
|
108
118
|
- ext/imprimatur_native/core/src/read/xlsx/formats.rs
|
|
109
119
|
- ext/imprimatur_native/core/src/script.rs
|
|
110
120
|
- ext/imprimatur_native/core/src/style.rs
|
|
111
|
-
- ext/imprimatur_native/core/src/test_support.rs
|
|
112
121
|
- ext/imprimatur_native/core/src/xlsx.rs
|
|
113
122
|
- ext/imprimatur_native/core/src/xlsx/autofit.rs
|
|
114
123
|
- ext/imprimatur_native/core/src/xlsx/cell.rs
|
|
@@ -128,6 +137,7 @@ files:
|
|
|
128
137
|
- ext/imprimatur_native/extconf.rb
|
|
129
138
|
- ext/imprimatur_native/ffi/Cargo.toml
|
|
130
139
|
- ext/imprimatur_native/ffi/src/archive.rs
|
|
140
|
+
- ext/imprimatur_native/ffi/src/barcode.rs
|
|
131
141
|
- ext/imprimatur_native/ffi/src/blocking.rs
|
|
132
142
|
- ext/imprimatur_native/ffi/src/convert.rs
|
|
133
143
|
- ext/imprimatur_native/ffi/src/document.rs
|
|
@@ -137,6 +147,7 @@ files:
|
|
|
137
147
|
- ext/imprimatur_native/ffi/src/style.rs
|
|
138
148
|
- ext/imprimatur_native/ffi/src/validation.rs
|
|
139
149
|
- lib/imprimatur.rb
|
|
150
|
+
- lib/imprimatur/barcode.rb
|
|
140
151
|
- lib/imprimatur/document.rb
|
|
141
152
|
- lib/imprimatur/error.rb
|
|
142
153
|
- lib/imprimatur/formula.rb
|
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
//! Helpers shared by the unit tests.
|
|
2
|
-
|
|
3
|
-
use std::io::{Cursor, Read};
|
|
4
|
-
|
|
5
|
-
/// Every entry of a zip archive as `(name, bytes)`, read with the `zip`
|
|
6
|
-
/// crate: an implementation independent of ours, which verifies each CRC.
|
|
7
|
-
pub fn read_zip(bytes: Vec<u8>) -> Vec<(String, Vec<u8>)> {
|
|
8
|
-
let mut archive = ::zip::ZipArchive::new(Cursor::new(bytes)).unwrap();
|
|
9
|
-
(0..archive.len())
|
|
10
|
-
.map(|index| {
|
|
11
|
-
let mut file = archive.by_index(index).unwrap();
|
|
12
|
-
let mut data = Vec::new();
|
|
13
|
-
file.read_to_end(&mut data).unwrap();
|
|
14
|
-
(file.name().unwrap().into_owned(), data)
|
|
15
|
-
})
|
|
16
|
-
.collect()
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/// Every entry of a zip archive as text, by name, read as [`read_zip`] does;
|
|
20
|
-
/// media come out garbled, for their names.
|
|
21
|
-
pub fn text_parts(bytes: Vec<u8>) -> Vec<(String, String)> {
|
|
22
|
-
read_zip(bytes)
|
|
23
|
-
.into_iter()
|
|
24
|
-
.map(|(name, data)| (name, String::from_utf8_lossy(&data).into_owned()))
|
|
25
|
-
.collect()
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/// The text of the part `name` among `parts`.
|
|
29
|
-
pub fn part<'a>(parts: &'a [(String, String)], name: &str) -> &'a str {
|
|
30
|
-
parts
|
|
31
|
-
.iter()
|
|
32
|
-
.find(|(part, _)| part == name)
|
|
33
|
-
.map_or_else(|| panic!("no part {name}"), |(_, xml)| xml)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/// A zip of `(name, text)` parts, written with our own writer.
|
|
37
|
-
pub fn zip_of(parts: &[(&str, &str)]) -> Vec<u8> {
|
|
38
|
-
let mut zip = crate::zip::ZipWriter::new(Vec::new());
|
|
39
|
-
for (name, text) in parts {
|
|
40
|
-
zip.add(name, text.as_bytes()).unwrap();
|
|
41
|
-
}
|
|
42
|
-
zip.finish().unwrap()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/// A PDF of `objects`, numbered from 1 (the first is the catalog), with a
|
|
46
|
-
/// classic cross-reference table.
|
|
47
|
-
pub fn pdf_of(objects: &[Vec<u8>]) -> Vec<u8> {
|
|
48
|
-
use std::io::Write as _;
|
|
49
|
-
let mut out = b"%PDF-1.7\n%\xE2\xE3\xCF\xD3\n".to_vec();
|
|
50
|
-
let mut offsets = Vec::new();
|
|
51
|
-
for (number, body) in (1..).zip(objects) {
|
|
52
|
-
offsets.push(out.len());
|
|
53
|
-
writeln!(out, "{number} 0 obj").unwrap();
|
|
54
|
-
out.extend_from_slice(body);
|
|
55
|
-
out.extend_from_slice(b"\nendobj\n");
|
|
56
|
-
}
|
|
57
|
-
let xref = out.len();
|
|
58
|
-
write!(out, "xref\n0 {}\n0000000000 65535 f \n", objects.len() + 1).unwrap();
|
|
59
|
-
for offset in offsets {
|
|
60
|
-
writeln!(out, "{offset:010} 00000 n ").unwrap();
|
|
61
|
-
}
|
|
62
|
-
write!(
|
|
63
|
-
out,
|
|
64
|
-
"trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n",
|
|
65
|
-
objects.len() + 1
|
|
66
|
-
)
|
|
67
|
-
.unwrap();
|
|
68
|
-
out
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/// A stream object: `dict` holds the entries besides `/Length`.
|
|
72
|
-
pub fn stream_object(dict: &str, data: &[u8]) -> Vec<u8> {
|
|
73
|
-
let mut out = format!("<< /Length {} {dict} >>\nstream\n", data.len()).into_bytes();
|
|
74
|
-
out.extend_from_slice(data);
|
|
75
|
-
out.extend_from_slice(b"\nendstream");
|
|
76
|
-
out
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/// One page of `content` with Helvetica as `/F1` and a 612 by 792 page.
|
|
80
|
-
pub fn page_pdf(content: &str, page_extra: &str) -> Vec<u8> {
|
|
81
|
-
pdf_of(&[
|
|
82
|
-
b"<< /Type /Catalog /Pages 2 0 R >>".to_vec(),
|
|
83
|
-
b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>".to_vec(),
|
|
84
|
-
format!(
|
|
85
|
-
"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] \
|
|
86
|
-
/Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R {page_extra} >>"
|
|
87
|
-
)
|
|
88
|
-
.into_bytes(),
|
|
89
|
-
b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>"
|
|
90
|
-
.to_vec(),
|
|
91
|
-
stream_object("", content.as_bytes()),
|
|
92
|
-
])
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/// A PNG as `read::image::png` writes it, read back.
|
|
96
|
-
#[derive(Debug)]
|
|
97
|
-
pub struct Png {
|
|
98
|
-
pub width: u32,
|
|
99
|
-
pub height: u32,
|
|
100
|
-
pub bits: u8,
|
|
101
|
-
pub color: u8,
|
|
102
|
-
pub palette: Option<Vec<u8>>,
|
|
103
|
-
/// The rows, their filter bytes (always 0, none) taken out.
|
|
104
|
-
pub rows: Vec<u8>,
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/// Reads a PNG, checking each chunk's CRC and that every row is unfiltered.
|
|
108
|
-
pub fn png_of(data: &[u8]) -> Png {
|
|
109
|
-
let be = |at: usize| u32::from_be_bytes(data[at..at + 4].try_into().unwrap());
|
|
110
|
-
assert!(data.starts_with(b"\x89PNG\r\n\x1a\n"), "{data:?}");
|
|
111
|
-
let (mut header, mut palette, mut compressed) = (Vec::new(), None, Vec::new());
|
|
112
|
-
let mut at = 8;
|
|
113
|
-
while at < data.len() {
|
|
114
|
-
let length = be(at) as usize;
|
|
115
|
-
let (kind, body) = (&data[at + 4..at + 8], &data[at + 8..at + 8 + length]);
|
|
116
|
-
assert_eq!(
|
|
117
|
-
be(at + 8 + length),
|
|
118
|
-
crc32fast::hash(&data[at + 4..at + 8 + length]),
|
|
119
|
-
"{kind:?}"
|
|
120
|
-
);
|
|
121
|
-
match kind {
|
|
122
|
-
b"IHDR" => header = body.to_vec(),
|
|
123
|
-
b"PLTE" => palette = Some(body.to_vec()),
|
|
124
|
-
b"IDAT" => compressed.extend_from_slice(body),
|
|
125
|
-
_ => {}
|
|
126
|
-
}
|
|
127
|
-
at += 12 + length;
|
|
128
|
-
}
|
|
129
|
-
let (width, height) = (be(16), be(20));
|
|
130
|
-
let (bits, color) = (header[8], header[9]);
|
|
131
|
-
let channels = match color {
|
|
132
|
-
0 | 3 => 1,
|
|
133
|
-
4 => 2,
|
|
134
|
-
2 => 3,
|
|
135
|
-
_ => 4,
|
|
136
|
-
};
|
|
137
|
-
let stride = (width as usize * channels * usize::from(bits)).div_ceil(8);
|
|
138
|
-
let filtered = miniz_oxide::inflate::decompress_to_vec_zlib(&compressed).unwrap();
|
|
139
|
-
assert_eq!(filtered.len(), (stride + 1) * height as usize);
|
|
140
|
-
let mut rows = Vec::new();
|
|
141
|
-
for row in filtered.chunks(stride + 1) {
|
|
142
|
-
assert_eq!(row[0], 0, "filter type");
|
|
143
|
-
rows.extend_from_slice(&row[1..]);
|
|
144
|
-
}
|
|
145
|
-
Png { width, height, bits, color, palette, rows }
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/// A one-strip little-endian TIFF's tags, by number, and its strip.
|
|
149
|
-
pub fn tiff_of(data: &[u8]) -> (std::collections::BTreeMap<u16, u32>, &[u8]) {
|
|
150
|
-
let le16 = |at: usize| u16::from_le_bytes(data[at..at + 2].try_into().unwrap());
|
|
151
|
-
let le32 = |at: usize| u32::from_le_bytes(data[at..at + 4].try_into().unwrap());
|
|
152
|
-
assert!(data.starts_with(b"II*\0"));
|
|
153
|
-
let directory = le32(4) as usize;
|
|
154
|
-
let tags: std::collections::BTreeMap<u16, u32> = (0..usize::from(le16(directory)))
|
|
155
|
-
.map(|index| {
|
|
156
|
-
let entry = directory + 2 + index * 12;
|
|
157
|
-
let value =
|
|
158
|
-
if le16(entry + 2) == 3 { u32::from(le16(entry + 8)) } else { le32(entry + 8) };
|
|
159
|
-
(le16(entry), value)
|
|
160
|
-
})
|
|
161
|
-
.collect();
|
|
162
|
-
let (offset, length) = (tags[&273] as usize, tags[&279] as usize);
|
|
163
|
-
(tags, &data[offset..offset + length])
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/// A compound file (MS-CFB, version 3) holding one stream named `name` in
|
|
167
|
-
/// its root storage; a stream under 4096 bytes goes to the mini stream, as
|
|
168
|
-
/// writers put it.
|
|
169
|
-
pub fn ole_of(name: &str, stream: &[u8]) -> Vec<u8> {
|
|
170
|
-
const SECTOR: usize = 512;
|
|
171
|
-
const END: u32 = 0xFFFF_FFFE;
|
|
172
|
-
let small = stream.len() < 4096;
|
|
173
|
-
let pad = |data: &[u8], size: usize| {
|
|
174
|
-
let mut data = data.to_vec();
|
|
175
|
-
data.resize(data.len().div_ceil(size).max(1) * size, 0);
|
|
176
|
-
data
|
|
177
|
-
};
|
|
178
|
-
// Sectors: FAT, directory, mini FAT (when small), then the data: the
|
|
179
|
-
// stream itself, or the mini stream holding it.
|
|
180
|
-
let data = if small { pad(stream, 64) } else { stream.to_vec() };
|
|
181
|
-
let data_sectors = data.len().div_ceil(SECTOR).max(1);
|
|
182
|
-
let first_data = if small { 3 } else { 2 };
|
|
183
|
-
let mut fat = vec![0xFFFF_FFFD_u32, END];
|
|
184
|
-
if small {
|
|
185
|
-
fat.push(END);
|
|
186
|
-
}
|
|
187
|
-
fat.extend((0..data_sectors).map(|index| {
|
|
188
|
-
if index + 1 == data_sectors { END } else { u32::try_from(first_data + index + 1).unwrap() }
|
|
189
|
-
}));
|
|
190
|
-
fat.resize(SECTOR / 4, 0xFFFF_FFFF);
|
|
191
|
-
let mut header = vec![0_u8; SECTOR];
|
|
192
|
-
header[..8].copy_from_slice(&[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]);
|
|
193
|
-
let put = |bytes: &mut Vec<u8>, at: usize, value: u32| {
|
|
194
|
-
bytes[at..at + 4].copy_from_slice(&value.to_le_bytes());
|
|
195
|
-
};
|
|
196
|
-
header[0x18..0x1A].copy_from_slice(&0x3E_u16.to_le_bytes());
|
|
197
|
-
header[0x1A..0x1C].copy_from_slice(&3_u16.to_le_bytes());
|
|
198
|
-
header[0x1C..0x1E].copy_from_slice(&0xFFFE_u16.to_le_bytes());
|
|
199
|
-
header[0x1E..0x20].copy_from_slice(&9_u16.to_le_bytes());
|
|
200
|
-
header[0x20..0x22].copy_from_slice(&6_u16.to_le_bytes());
|
|
201
|
-
put(&mut header, 0x2C, 1);
|
|
202
|
-
put(&mut header, 0x30, 1);
|
|
203
|
-
put(&mut header, 0x38, 4096);
|
|
204
|
-
put(&mut header, 0x3C, if small { 2 } else { END });
|
|
205
|
-
put(&mut header, 0x40, u32::from(small));
|
|
206
|
-
put(&mut header, 0x44, END);
|
|
207
|
-
put(&mut header, 0x4C, 0);
|
|
208
|
-
for index in 1..109 {
|
|
209
|
-
put(&mut header, 0x4C + index * 4, 0xFFFF_FFFF);
|
|
210
|
-
}
|
|
211
|
-
let entry = |name: &str, kind: u8, child: u32, start: u32, size: usize| {
|
|
212
|
-
let mut bytes = vec![0_u8; 128];
|
|
213
|
-
let units: Vec<u16> = name.encode_utf16().collect();
|
|
214
|
-
for (index, unit) in units.iter().enumerate() {
|
|
215
|
-
bytes[index * 2..index * 2 + 2].copy_from_slice(&unit.to_le_bytes());
|
|
216
|
-
}
|
|
217
|
-
bytes[0x40..0x42]
|
|
218
|
-
.copy_from_slice(&u16::try_from(units.len() * 2 + 2).unwrap().to_le_bytes());
|
|
219
|
-
bytes[0x42] = kind;
|
|
220
|
-
for at in [0x44, 0x48, 0x4C] {
|
|
221
|
-
put(&mut bytes, at, 0xFFFF_FFFF);
|
|
222
|
-
}
|
|
223
|
-
put(&mut bytes, 0x4C, child);
|
|
224
|
-
put(&mut bytes, 0x74, start);
|
|
225
|
-
put(&mut bytes, 0x78, u32::try_from(size).unwrap());
|
|
226
|
-
bytes
|
|
227
|
-
};
|
|
228
|
-
let first = u32::try_from(first_data).unwrap();
|
|
229
|
-
let mut directory = if small {
|
|
230
|
-
let mut directory = entry("Root Entry", 5, 1, first, data.len());
|
|
231
|
-
directory.extend(entry(name, 2, 0xFFFF_FFFF, 0, stream.len()));
|
|
232
|
-
directory
|
|
233
|
-
} else {
|
|
234
|
-
let mut directory = entry("Root Entry", 5, 1, END, 0);
|
|
235
|
-
directory.extend(entry(name, 2, 0xFFFF_FFFF, first, stream.len()));
|
|
236
|
-
directory
|
|
237
|
-
};
|
|
238
|
-
directory.resize(SECTOR, 0);
|
|
239
|
-
let mut file = header;
|
|
240
|
-
file.extend(fat.iter().flat_map(|word| word.to_le_bytes()));
|
|
241
|
-
file.extend(directory);
|
|
242
|
-
if small {
|
|
243
|
-
let mut mini_fat: Vec<u32> =
|
|
244
|
-
(1..data.len() / 64).map(|next| u32::try_from(next).unwrap()).collect();
|
|
245
|
-
mini_fat.push(END);
|
|
246
|
-
mini_fat.resize(SECTOR / 4, 0xFFFF_FFFF);
|
|
247
|
-
file.extend(mini_fat.iter().flat_map(|word| word.to_le_bytes()));
|
|
248
|
-
}
|
|
249
|
-
file.extend(pad(&data, SECTOR));
|
|
250
|
-
file
|
|
251
|
-
}
|