blitz-pdf 0.1.1 → 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.
Files changed (114) hide show
  1. package/README.md +266 -12
  2. package/THIRD_PARTY_NOTICES.md +4 -2
  3. package/dist/browser.d.ts +1 -1
  4. package/dist/browser.d.ts.map +1 -1
  5. package/dist/document-options.d.ts +36 -0
  6. package/dist/document-options.d.ts.map +1 -0
  7. package/dist/document-options.js +236 -0
  8. package/dist/document-options.js.map +1 -0
  9. package/dist/node.d.ts +1 -1
  10. package/dist/node.d.ts.map +1 -1
  11. package/dist/options.d.ts +35 -1
  12. package/dist/options.d.ts.map +1 -1
  13. package/dist/options.js +21 -14
  14. package/dist/options.js.map +1 -1
  15. package/dist/types.d.ts +71 -1
  16. package/dist/types.d.ts.map +1 -1
  17. package/dist/validation.d.ts +2 -0
  18. package/dist/validation.d.ts.map +1 -0
  19. package/dist/validation.js +7 -0
  20. package/dist/validation.js.map +1 -0
  21. package/examples/zugferd.mjs +41 -0
  22. package/licenses/RUST_DEPENDENCIES.txt +27 -0
  23. package/package.json +3 -2
  24. package/src/browser.ts +1 -1
  25. package/src/document-options.ts +218 -0
  26. package/src/node.ts +1 -1
  27. package/src/options.ts +18 -13
  28. package/src/types.ts +76 -1
  29. package/src/validation.ts +6 -0
  30. package/vendor/README.md +27 -2
  31. package/vendor/krilla/.cargo-ok +1 -0
  32. package/vendor/krilla/.cargo_vcs_info.json +6 -0
  33. package/vendor/krilla/Cargo.lock +1357 -0
  34. package/vendor/krilla/Cargo.toml +165 -0
  35. package/vendor/krilla/LICENSE_APACHE +202 -0
  36. package/vendor/krilla/LICENSE_MIT +21 -0
  37. package/vendor/krilla/README.md +156 -0
  38. package/vendor/krilla/examples/basic.rs +117 -0
  39. package/vendor/krilla/examples/empty_document.rs +25 -0
  40. package/vendor/krilla/examples/parley.rs +174 -0
  41. package/vendor/krilla/examples/simple_text.rs +118 -0
  42. package/vendor/krilla/examples/stream_builder.rs +93 -0
  43. package/vendor/krilla/icc/LICENSE +116 -0
  44. package/vendor/krilla/icc/README.md +1 -0
  45. package/vendor/krilla/icc/sGrey-v2-magic.icc +0 -0
  46. package/vendor/krilla/icc/sGrey-v4.icc +0 -0
  47. package/vendor/krilla/icc/sRGB-v2-magic.icc +0 -0
  48. package/vendor/krilla/icc/sRGB-v4.icc +0 -0
  49. package/vendor/krilla/src/chunk_container.rs +503 -0
  50. package/vendor/krilla/src/configure/PDF_A1.md +227 -0
  51. package/vendor/krilla/src/configure/PDF_A2.md +269 -0
  52. package/vendor/krilla/src/configure/PDF_A3.md +31 -0
  53. package/vendor/krilla/src/configure/PDF_A4.md +211 -0
  54. package/vendor/krilla/src/configure/PDF_UA1.md +202 -0
  55. package/vendor/krilla/src/configure/README.md +23 -0
  56. package/vendor/krilla/src/configure/mod.rs +197 -0
  57. package/vendor/krilla/src/configure/validate.rs +1244 -0
  58. package/vendor/krilla/src/configure/version.rs +134 -0
  59. package/vendor/krilla/src/content.rs +1352 -0
  60. package/vendor/krilla/src/data.rs +49 -0
  61. package/vendor/krilla/src/document.rs +156 -0
  62. package/vendor/krilla/src/error.rs +170 -0
  63. package/vendor/krilla/src/geom.rs +341 -0
  64. package/vendor/krilla/src/graphics/blend.rs +46 -0
  65. package/vendor/krilla/src/graphics/color.rs +552 -0
  66. package/vendor/krilla/src/graphics/graphic.rs +30 -0
  67. package/vendor/krilla/src/graphics/graphics_state.rs +251 -0
  68. package/vendor/krilla/src/graphics/icc.rs +218 -0
  69. package/vendor/krilla/src/graphics/image.rs +835 -0
  70. package/vendor/krilla/src/graphics/mask.rs +128 -0
  71. package/vendor/krilla/src/graphics/mod.rs +14 -0
  72. package/vendor/krilla/src/graphics/paint.rs +434 -0
  73. package/vendor/krilla/src/graphics/separation.rs +86 -0
  74. package/vendor/krilla/src/graphics/shading_function.rs +865 -0
  75. package/vendor/krilla/src/graphics/shading_pattern.rs +60 -0
  76. package/vendor/krilla/src/graphics/tiling_pattern.rs +119 -0
  77. package/vendor/krilla/src/graphics/xobject.rs +150 -0
  78. package/vendor/krilla/src/interactive/action.rs +68 -0
  79. package/vendor/krilla/src/interactive/annotation.rs +283 -0
  80. package/vendor/krilla/src/interactive/destination.rs +289 -0
  81. package/vendor/krilla/src/interactive/mod.rs +3 -0
  82. package/vendor/krilla/src/interchange/embed.rs +240 -0
  83. package/vendor/krilla/src/interchange/metadata.rs +564 -0
  84. package/vendor/krilla/src/interchange/mod.rs +4 -0
  85. package/vendor/krilla/src/interchange/outline.rs +263 -0
  86. package/vendor/krilla/src/interchange/tagging/fmt.rs +733 -0
  87. package/vendor/krilla/src/interchange/tagging/generate.toml +474 -0
  88. package/vendor/krilla/src/interchange/tagging/generated.rs +4754 -0
  89. package/vendor/krilla/src/interchange/tagging/mod.rs +1324 -0
  90. package/vendor/krilla/src/interchange/tagging/tag.rs +686 -0
  91. package/vendor/krilla/src/lib.rs +179 -0
  92. package/vendor/krilla/src/num.rs +26 -0
  93. package/vendor/krilla/src/page.rs +612 -0
  94. package/vendor/krilla/src/pdf.rs +313 -0
  95. package/vendor/krilla/src/resource.rs +436 -0
  96. package/vendor/krilla/src/serialize.rs +1101 -0
  97. package/vendor/krilla/src/stream.rs +368 -0
  98. package/vendor/krilla/src/surface.rs +664 -0
  99. package/vendor/krilla/src/text/cid.rs +488 -0
  100. package/vendor/krilla/src/text/font.rs +351 -0
  101. package/vendor/krilla/src/text/glyph/bitmap.rs +81 -0
  102. package/vendor/krilla/src/text/glyph/colr.rs +581 -0
  103. package/vendor/krilla/src/text/glyph/mod.rs +205 -0
  104. package/vendor/krilla/src/text/glyph/outline.rs +72 -0
  105. package/vendor/krilla/src/text/glyph/svg.rs +34 -0
  106. package/vendor/krilla/src/text/group.rs +463 -0
  107. package/vendor/krilla/src/text/mod.rs +232 -0
  108. package/vendor/krilla/src/text/shape.rs +102 -0
  109. package/vendor/krilla/src/text/type3.rs +514 -0
  110. package/vendor/krilla/src/util.rs +286 -0
  111. package/vendor/stylo/properties/longhands.toml +0 -1
  112. package/vendor/stylo/stylesheets/rule_parser.rs +1 -1
  113. package/wasm/blitz_pdf.js +10 -5
  114. 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
+ }