blitz-pdf 0.1.0 → 0.1.2
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.
- package/README.md +300 -10
- package/THIRD_PARTY_NOTICES.md +4 -2
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/document-options.d.ts +36 -0
- package/dist/document-options.d.ts.map +1 -0
- package/dist/document-options.js +236 -0
- package/dist/document-options.js.map +1 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/options.d.ts +35 -1
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +21 -14
- package/dist/options.js.map +1 -1
- package/dist/types.d.ts +71 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +2 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +7 -0
- package/dist/validation.js.map +1 -0
- package/examples/zugferd.mjs +41 -0
- package/licenses/RUST_DEPENDENCIES.txt +27 -0
- package/package.json +3 -2
- package/src/browser.ts +1 -1
- package/src/document-options.ts +218 -0
- package/src/node.ts +1 -1
- package/src/options.ts +18 -13
- package/src/types.ts +76 -1
- package/src/validation.ts +6 -0
- package/vendor/README.md +27 -2
- package/vendor/krilla/.cargo-ok +1 -0
- package/vendor/krilla/.cargo_vcs_info.json +6 -0
- package/vendor/krilla/Cargo.lock +1357 -0
- package/vendor/krilla/Cargo.toml +165 -0
- package/vendor/krilla/LICENSE_APACHE +202 -0
- package/vendor/krilla/LICENSE_MIT +21 -0
- package/vendor/krilla/README.md +156 -0
- package/vendor/krilla/examples/basic.rs +117 -0
- package/vendor/krilla/examples/empty_document.rs +25 -0
- package/vendor/krilla/examples/parley.rs +174 -0
- package/vendor/krilla/examples/simple_text.rs +118 -0
- package/vendor/krilla/examples/stream_builder.rs +93 -0
- package/vendor/krilla/icc/LICENSE +116 -0
- package/vendor/krilla/icc/README.md +1 -0
- package/vendor/krilla/icc/sGrey-v2-magic.icc +0 -0
- package/vendor/krilla/icc/sGrey-v4.icc +0 -0
- package/vendor/krilla/icc/sRGB-v2-magic.icc +0 -0
- package/vendor/krilla/icc/sRGB-v4.icc +0 -0
- package/vendor/krilla/src/chunk_container.rs +503 -0
- package/vendor/krilla/src/configure/PDF_A1.md +227 -0
- package/vendor/krilla/src/configure/PDF_A2.md +269 -0
- package/vendor/krilla/src/configure/PDF_A3.md +31 -0
- package/vendor/krilla/src/configure/PDF_A4.md +211 -0
- package/vendor/krilla/src/configure/PDF_UA1.md +202 -0
- package/vendor/krilla/src/configure/README.md +23 -0
- package/vendor/krilla/src/configure/mod.rs +197 -0
- package/vendor/krilla/src/configure/validate.rs +1244 -0
- package/vendor/krilla/src/configure/version.rs +134 -0
- package/vendor/krilla/src/content.rs +1352 -0
- package/vendor/krilla/src/data.rs +49 -0
- package/vendor/krilla/src/document.rs +156 -0
- package/vendor/krilla/src/error.rs +170 -0
- package/vendor/krilla/src/geom.rs +341 -0
- package/vendor/krilla/src/graphics/blend.rs +46 -0
- package/vendor/krilla/src/graphics/color.rs +552 -0
- package/vendor/krilla/src/graphics/graphic.rs +30 -0
- package/vendor/krilla/src/graphics/graphics_state.rs +251 -0
- package/vendor/krilla/src/graphics/icc.rs +218 -0
- package/vendor/krilla/src/graphics/image.rs +835 -0
- package/vendor/krilla/src/graphics/mask.rs +128 -0
- package/vendor/krilla/src/graphics/mod.rs +14 -0
- package/vendor/krilla/src/graphics/paint.rs +434 -0
- package/vendor/krilla/src/graphics/separation.rs +86 -0
- package/vendor/krilla/src/graphics/shading_function.rs +865 -0
- package/vendor/krilla/src/graphics/shading_pattern.rs +60 -0
- package/vendor/krilla/src/graphics/tiling_pattern.rs +119 -0
- package/vendor/krilla/src/graphics/xobject.rs +150 -0
- package/vendor/krilla/src/interactive/action.rs +68 -0
- package/vendor/krilla/src/interactive/annotation.rs +283 -0
- package/vendor/krilla/src/interactive/destination.rs +289 -0
- package/vendor/krilla/src/interactive/mod.rs +3 -0
- package/vendor/krilla/src/interchange/embed.rs +240 -0
- package/vendor/krilla/src/interchange/metadata.rs +564 -0
- package/vendor/krilla/src/interchange/mod.rs +4 -0
- package/vendor/krilla/src/interchange/outline.rs +263 -0
- package/vendor/krilla/src/interchange/tagging/fmt.rs +733 -0
- package/vendor/krilla/src/interchange/tagging/generate.toml +474 -0
- package/vendor/krilla/src/interchange/tagging/generated.rs +4754 -0
- package/vendor/krilla/src/interchange/tagging/mod.rs +1324 -0
- package/vendor/krilla/src/interchange/tagging/tag.rs +686 -0
- package/vendor/krilla/src/lib.rs +179 -0
- package/vendor/krilla/src/num.rs +26 -0
- package/vendor/krilla/src/page.rs +612 -0
- package/vendor/krilla/src/pdf.rs +313 -0
- package/vendor/krilla/src/resource.rs +436 -0
- package/vendor/krilla/src/serialize.rs +1101 -0
- package/vendor/krilla/src/stream.rs +368 -0
- package/vendor/krilla/src/surface.rs +664 -0
- package/vendor/krilla/src/text/cid.rs +488 -0
- package/vendor/krilla/src/text/font.rs +351 -0
- package/vendor/krilla/src/text/glyph/bitmap.rs +81 -0
- package/vendor/krilla/src/text/glyph/colr.rs +581 -0
- package/vendor/krilla/src/text/glyph/mod.rs +205 -0
- package/vendor/krilla/src/text/glyph/outline.rs +72 -0
- package/vendor/krilla/src/text/glyph/svg.rs +34 -0
- package/vendor/krilla/src/text/group.rs +463 -0
- package/vendor/krilla/src/text/mod.rs +232 -0
- package/vendor/krilla/src/text/shape.rs +102 -0
- package/vendor/krilla/src/text/type3.rs +514 -0
- package/vendor/krilla/src/util.rs +286 -0
- package/vendor/stylo/properties/longhands.toml +0 -1
- package/vendor/stylo/stylesheets/rule_parser.rs +1 -1
- package/wasm/blitz_pdf.js +10 -5
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
//! Drawing to a non-page context.
|
|
2
|
+
//!
|
|
3
|
+
//! In 90% of the cases, you will not have to use streams. In most cases,
|
|
4
|
+
//! all you need to do when using this crate is to first construct a document,
|
|
5
|
+
//! and then add new pages to the document and use the [`Page::surface`] method to get
|
|
6
|
+
//! access to the drawing context. However, there are cases when you don't want to
|
|
7
|
+
//! draw on the main page surface, but instead you want to create a "sub-surface"
|
|
8
|
+
//! where you can draw independently of the main page contents. This is what streams
|
|
9
|
+
//! are there for. Currently, there are only two situations where you need to do that:
|
|
10
|
+
//!
|
|
11
|
+
//! - When using masks and defining the contents of the mask.
|
|
12
|
+
//! - When using a pattern fill or stroke and defining the contents of the pattern.
|
|
13
|
+
//!
|
|
14
|
+
//! If you want to do any of the two above, you need to call the [`Surface::stream_builder`] method
|
|
15
|
+
//! of the current surface. The stream builder represents a kind of sub-context that is
|
|
16
|
+
//! independent of the main surface you are working with. Once you have a stream builder, you
|
|
17
|
+
//! can once again invoke the [`StreamBuilder::surface`] method, and use this new surface to define the contents
|
|
18
|
+
//! of your mask/pattern. In the end, you can call [`StreamBuilder::finish`] which will return a [`Stream`] object.
|
|
19
|
+
//! This [`Stream`] object contains the encoded instructions of the mask/pattern, which you can
|
|
20
|
+
//! then use to create new [`Pattern`]/[`Mask`] objects.
|
|
21
|
+
//!
|
|
22
|
+
//! [`Page::surface`]: crate::page::Page::surface
|
|
23
|
+
//! [`Surface::stream_builder`]: crate::surface::Surface::stream_builder
|
|
24
|
+
//! [`Pattern`]: crate::graphics::paint::Pattern
|
|
25
|
+
//! [`Mask`]: crate::graphics::mask::Mask
|
|
26
|
+
|
|
27
|
+
use std::borrow::Cow;
|
|
28
|
+
use std::ops::DerefMut;
|
|
29
|
+
|
|
30
|
+
use flate2::write::ZlibEncoder;
|
|
31
|
+
use flate2::Compression;
|
|
32
|
+
use pdf_writer::{Array, Dict, Name};
|
|
33
|
+
|
|
34
|
+
use crate::chunk_container::ChunkContainer;
|
|
35
|
+
use crate::configure::ValidationError;
|
|
36
|
+
use crate::content::ContentBuilder;
|
|
37
|
+
use crate::geom::{Rect, Transform};
|
|
38
|
+
use crate::resource::{ResourceDictionary, ResourceDictionaryBuilder};
|
|
39
|
+
use crate::serialize::SerializeContext;
|
|
40
|
+
use crate::surface::Surface;
|
|
41
|
+
use crate::SerializeSettings;
|
|
42
|
+
|
|
43
|
+
/// A stream.
|
|
44
|
+
///
|
|
45
|
+
/// See the module description for an explanation of its purpose.
|
|
46
|
+
// The only reason we implement clone for this type is that in some cases,
|
|
47
|
+
// we might need to clone a pattern (including its stream)
|
|
48
|
+
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
|
|
49
|
+
pub struct Stream {
|
|
50
|
+
pub(crate) content: Vec<u8>,
|
|
51
|
+
pub(crate) bbox: Rect,
|
|
52
|
+
// Important: Each object that uses a stream must ensure to pass on the validation
|
|
53
|
+
// errors to the `SerializeContext` at some point. Currently, only `Mask`,
|
|
54
|
+
// `TilingPattern`, `InternalPage` and `XObject` require that.
|
|
55
|
+
pub(crate) validation_errors: Vec<ValidationError>,
|
|
56
|
+
pub(crate) resource_dictionary: ResourceDictionary,
|
|
57
|
+
pub(crate) uses_mask: bool,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl Stream {
|
|
61
|
+
pub(crate) fn new(
|
|
62
|
+
content: Vec<u8>,
|
|
63
|
+
bbox: Rect,
|
|
64
|
+
validation_errors: Vec<ValidationError>,
|
|
65
|
+
resource_dictionary: ResourceDictionary,
|
|
66
|
+
uses_mask: bool,
|
|
67
|
+
) -> Self {
|
|
68
|
+
Self {
|
|
69
|
+
content,
|
|
70
|
+
bbox,
|
|
71
|
+
validation_errors,
|
|
72
|
+
resource_dictionary,
|
|
73
|
+
uses_mask,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pub(crate) fn is_empty(&self) -> bool {
|
|
78
|
+
self.content.is_empty()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub(crate) fn empty() -> Self {
|
|
82
|
+
Self {
|
|
83
|
+
content: vec![],
|
|
84
|
+
bbox: Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap(),
|
|
85
|
+
validation_errors: vec![],
|
|
86
|
+
resource_dictionary: ResourceDictionaryBuilder::new().finish(),
|
|
87
|
+
uses_mask: false,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// A builder to create streams.
|
|
93
|
+
pub struct StreamBuilder<'a> {
|
|
94
|
+
sc: &'a mut SerializeContext,
|
|
95
|
+
chunk_container: &'a mut ChunkContainer,
|
|
96
|
+
stream: Stream,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
impl<'a> StreamBuilder<'a> {
|
|
100
|
+
pub(crate) fn new(
|
|
101
|
+
sc: &'a mut SerializeContext,
|
|
102
|
+
chunk_container: &'a mut ChunkContainer,
|
|
103
|
+
) -> Self {
|
|
104
|
+
Self {
|
|
105
|
+
sc,
|
|
106
|
+
chunk_container,
|
|
107
|
+
stream: Stream::empty(),
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Get the surface of the stream builder.
|
|
112
|
+
pub fn surface(&mut self) -> Surface<'_> {
|
|
113
|
+
// Stream builders cannot have any tags since we always pass a dummy
|
|
114
|
+
// identifier. Only main page content streams can have one.
|
|
115
|
+
let finish_fn = Box::new(|stream, _| {
|
|
116
|
+
self.stream = stream;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
Surface::new(
|
|
120
|
+
self.sc,
|
|
121
|
+
self.chunk_container,
|
|
122
|
+
ContentBuilder::new(Transform::identity(), true, self.sc),
|
|
123
|
+
None,
|
|
124
|
+
finish_fn,
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// Turn the stream builder into a stream.
|
|
129
|
+
pub fn finish(self) -> Stream {
|
|
130
|
+
self.stream
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// A PDF stream filter.
|
|
135
|
+
#[derive(Debug, Copy, Clone)]
|
|
136
|
+
pub(crate) enum StreamFilter {
|
|
137
|
+
Flate,
|
|
138
|
+
FlateMemoized,
|
|
139
|
+
AsciiHex,
|
|
140
|
+
#[cfg(feature = "raster-images")]
|
|
141
|
+
Dct,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
impl StreamFilter {
|
|
145
|
+
pub(crate) fn to_name(self) -> Name<'static> {
|
|
146
|
+
match self {
|
|
147
|
+
Self::AsciiHex => Name(b"ASCIIHexDecode"),
|
|
148
|
+
Self::Flate => Name(b"FlateDecode"),
|
|
149
|
+
Self::FlateMemoized => Name(b"FlateDecode"),
|
|
150
|
+
#[cfg(feature = "raster-images")]
|
|
151
|
+
Self::Dct => Name(b"DCTDecode"),
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
pub(crate) fn is_binary(&self) -> bool {
|
|
156
|
+
match self {
|
|
157
|
+
StreamFilter::Flate => true,
|
|
158
|
+
StreamFilter::FlateMemoized => true,
|
|
159
|
+
StreamFilter::AsciiHex => false,
|
|
160
|
+
#[cfg(feature = "raster-images")]
|
|
161
|
+
StreamFilter::Dct => true,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
impl StreamFilter {
|
|
167
|
+
#[track_caller]
|
|
168
|
+
pub(crate) fn apply(&self, content: &[u8]) -> Vec<u8> {
|
|
169
|
+
match self {
|
|
170
|
+
StreamFilter::Flate => deflate_encode(content),
|
|
171
|
+
StreamFilter::FlateMemoized => deflate_encode_memoized(content),
|
|
172
|
+
StreamFilter::AsciiHex => hex_encode(content),
|
|
173
|
+
// Note: We don't actually encode manually with DCT, because
|
|
174
|
+
// this is only used for JPEG images which are already encoded,
|
|
175
|
+
// so this shouldn't be called at all.
|
|
176
|
+
#[cfg(feature = "raster-images")]
|
|
177
|
+
StreamFilter::Dct => panic!("can't apply dct decode"),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Allows us to keep track of the filters that a stream has and
|
|
183
|
+
/// apply them in an orderly fashion.
|
|
184
|
+
#[derive(Debug, Clone)]
|
|
185
|
+
pub(crate) enum StreamFilters {
|
|
186
|
+
None,
|
|
187
|
+
Single(StreamFilter),
|
|
188
|
+
Multiple(Vec<StreamFilter>),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
impl StreamFilters {
|
|
192
|
+
pub(crate) fn add(&mut self, stream_filter: StreamFilter) {
|
|
193
|
+
match self {
|
|
194
|
+
StreamFilters::None => *self = StreamFilters::Single(stream_filter),
|
|
195
|
+
StreamFilters::Single(cur) => {
|
|
196
|
+
*self = StreamFilters::Multiple(vec![*cur, stream_filter])
|
|
197
|
+
}
|
|
198
|
+
StreamFilters::Multiple(cur) => cur.push(stream_filter),
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
pub(crate) fn is_binary(&self) -> bool {
|
|
203
|
+
match self {
|
|
204
|
+
StreamFilters::None => false,
|
|
205
|
+
StreamFilters::Single(s) => s.is_binary(),
|
|
206
|
+
StreamFilters::Multiple(m) => m.last().unwrap().is_binary(),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pub(crate) struct FilterStreamBuilder<'a> {
|
|
212
|
+
content: Cow<'a, [u8]>,
|
|
213
|
+
filters: StreamFilters,
|
|
214
|
+
is_binary: bool,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
impl<'a> FilterStreamBuilder<'a> {
|
|
218
|
+
fn empty(content: &'a [u8]) -> Self {
|
|
219
|
+
Self {
|
|
220
|
+
content: Cow::Borrowed(content),
|
|
221
|
+
filters: StreamFilters::None,
|
|
222
|
+
is_binary: false,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
fn binary(content: &'a [u8]) -> Self {
|
|
227
|
+
Self {
|
|
228
|
+
is_binary: true,
|
|
229
|
+
..Self::empty(content)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
pub(crate) fn new_from_content_stream(
|
|
234
|
+
content: &'a [u8],
|
|
235
|
+
serialize_settings: &SerializeSettings,
|
|
236
|
+
) -> Self {
|
|
237
|
+
let mut filter_stream = Self::empty(content);
|
|
238
|
+
|
|
239
|
+
if serialize_settings.compress_content_streams {
|
|
240
|
+
filter_stream.add_filter(StreamFilter::FlateMemoized);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
filter_stream
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
pub(crate) fn new_from_deflated(content: &'a [u8]) -> Self {
|
|
247
|
+
let mut filter_stream = Self::empty(content);
|
|
248
|
+
filter_stream.add_unapplied_filter(StreamFilter::Flate);
|
|
249
|
+
|
|
250
|
+
filter_stream
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
pub(crate) fn new_from_binary_data(content: &'a [u8]) -> Self {
|
|
254
|
+
let mut filter_stream = Self::binary(content);
|
|
255
|
+
filter_stream.add_filter(StreamFilter::Flate);
|
|
256
|
+
|
|
257
|
+
filter_stream
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
pub(crate) fn new_auto_compressed(content: &'a [u8]) -> Self {
|
|
261
|
+
let filter_stream = Self::new_from_binary_data(content);
|
|
262
|
+
|
|
263
|
+
const MAX_COMPRESSED_SIZE: usize = 75;
|
|
264
|
+
if content.is_empty()
|
|
265
|
+
|| 100 * filter_stream.content.len() / content.len() > MAX_COMPRESSED_SIZE
|
|
266
|
+
{
|
|
267
|
+
return Self::binary(content);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
filter_stream
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
pub(crate) fn new_from_uncompressed(content: &'a [u8]) -> Self {
|
|
274
|
+
Self::binary(content)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
#[cfg(feature = "raster-images")]
|
|
278
|
+
pub(crate) fn new_from_jpeg_data(content: &'a [u8]) -> Self {
|
|
279
|
+
let mut filter_stream = Self::empty(content);
|
|
280
|
+
// JPEG data already is DCT encoded.
|
|
281
|
+
filter_stream.add_unapplied_filter(StreamFilter::Dct);
|
|
282
|
+
|
|
283
|
+
filter_stream
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
pub(crate) fn finish(mut self, serialize_settings: &SerializeSettings) -> FilterStream<'a> {
|
|
287
|
+
if serialize_settings.ascii_compatible
|
|
288
|
+
&& !self.content.is_empty()
|
|
289
|
+
&& (self.filters.is_binary()
|
|
290
|
+
|| (self.is_binary && self.content.iter().any(|byte| !byte.is_ascii())))
|
|
291
|
+
{
|
|
292
|
+
self.add_filter(StreamFilter::AsciiHex);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
FilterStream {
|
|
296
|
+
content: self.content,
|
|
297
|
+
filters: self.filters,
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
fn add_filter(&mut self, filter: StreamFilter) {
|
|
302
|
+
self.content = Cow::Owned(filter.apply(&self.content));
|
|
303
|
+
self.filters.add(filter);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
fn add_unapplied_filter(&mut self, filter: StreamFilter) {
|
|
307
|
+
self.filters.add(filter);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
pub(crate) struct FilterStream<'a> {
|
|
312
|
+
content: Cow<'a, [u8]>,
|
|
313
|
+
filters: StreamFilters,
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
impl FilterStream<'_> {
|
|
317
|
+
pub(crate) fn encoded_data(&self) -> &[u8] {
|
|
318
|
+
&self.content
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
pub(crate) fn write_filters<'b, T>(&self, mut dict: T)
|
|
322
|
+
where
|
|
323
|
+
T: DerefMut<Target = Dict<'b>>,
|
|
324
|
+
{
|
|
325
|
+
match &self.filters {
|
|
326
|
+
StreamFilters::None => {}
|
|
327
|
+
StreamFilters::Single(filter) => {
|
|
328
|
+
dict.deref_mut().pair(Name(b"Filter"), filter.to_name());
|
|
329
|
+
}
|
|
330
|
+
StreamFilters::Multiple(filters) => {
|
|
331
|
+
dict.deref_mut()
|
|
332
|
+
.insert(Name(b"Filter"))
|
|
333
|
+
.start::<Array>()
|
|
334
|
+
.items(filters.iter().map(|f| f.to_name()).rev());
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#[cfg_attr(feature = "comemo", comemo::memoize)]
|
|
341
|
+
fn deflate_encode_memoized(data: &[u8]) -> Vec<u8> {
|
|
342
|
+
deflate_encode(data)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
pub(crate) fn deflate_encode(data: &[u8]) -> Vec<u8> {
|
|
346
|
+
use std::io::Write;
|
|
347
|
+
|
|
348
|
+
const COMPRESSION_LEVEL: u8 = 6;
|
|
349
|
+
let mut e = ZlibEncoder::new(Vec::new(), Compression::new(COMPRESSION_LEVEL as u32));
|
|
350
|
+
e.write_all(data).unwrap();
|
|
351
|
+
e.finish().unwrap()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
fn hex_encode(data: &[u8]) -> Vec<u8> {
|
|
355
|
+
use std::fmt::Write;
|
|
356
|
+
|
|
357
|
+
const COLUMNS: usize = 35;
|
|
358
|
+
let len = data.len();
|
|
359
|
+
let capacity = 2 * len + len / COLUMNS;
|
|
360
|
+
let mut output = String::with_capacity(capacity);
|
|
361
|
+
for (index, byte) in data.iter().enumerate() {
|
|
362
|
+
write!(output, "{byte:02X}").unwrap();
|
|
363
|
+
if index % COLUMNS == COLUMNS - 1 {
|
|
364
|
+
output.push('\n');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
output.into_bytes()
|
|
368
|
+
}
|