blitz-pdf 0.1.1 → 0.1.3

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 (115) hide show
  1. package/README.md +292 -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/docs/pagination-regressions.md +88 -0
  22. package/examples/zugferd.mjs +41 -0
  23. package/licenses/RUST_DEPENDENCIES.txt +27 -0
  24. package/package.json +4 -2
  25. package/src/browser.ts +1 -1
  26. package/src/document-options.ts +218 -0
  27. package/src/node.ts +1 -1
  28. package/src/options.ts +18 -13
  29. package/src/types.ts +76 -1
  30. package/src/validation.ts +6 -0
  31. package/vendor/README.md +27 -2
  32. package/vendor/krilla/.cargo-ok +1 -0
  33. package/vendor/krilla/.cargo_vcs_info.json +6 -0
  34. package/vendor/krilla/Cargo.lock +1357 -0
  35. package/vendor/krilla/Cargo.toml +165 -0
  36. package/vendor/krilla/LICENSE_APACHE +202 -0
  37. package/vendor/krilla/LICENSE_MIT +21 -0
  38. package/vendor/krilla/README.md +156 -0
  39. package/vendor/krilla/examples/basic.rs +117 -0
  40. package/vendor/krilla/examples/empty_document.rs +25 -0
  41. package/vendor/krilla/examples/parley.rs +174 -0
  42. package/vendor/krilla/examples/simple_text.rs +118 -0
  43. package/vendor/krilla/examples/stream_builder.rs +93 -0
  44. package/vendor/krilla/icc/LICENSE +116 -0
  45. package/vendor/krilla/icc/README.md +1 -0
  46. package/vendor/krilla/icc/sGrey-v2-magic.icc +0 -0
  47. package/vendor/krilla/icc/sGrey-v4.icc +0 -0
  48. package/vendor/krilla/icc/sRGB-v2-magic.icc +0 -0
  49. package/vendor/krilla/icc/sRGB-v4.icc +0 -0
  50. package/vendor/krilla/src/chunk_container.rs +503 -0
  51. package/vendor/krilla/src/configure/PDF_A1.md +227 -0
  52. package/vendor/krilla/src/configure/PDF_A2.md +269 -0
  53. package/vendor/krilla/src/configure/PDF_A3.md +31 -0
  54. package/vendor/krilla/src/configure/PDF_A4.md +211 -0
  55. package/vendor/krilla/src/configure/PDF_UA1.md +202 -0
  56. package/vendor/krilla/src/configure/README.md +23 -0
  57. package/vendor/krilla/src/configure/mod.rs +197 -0
  58. package/vendor/krilla/src/configure/validate.rs +1244 -0
  59. package/vendor/krilla/src/configure/version.rs +134 -0
  60. package/vendor/krilla/src/content.rs +1352 -0
  61. package/vendor/krilla/src/data.rs +49 -0
  62. package/vendor/krilla/src/document.rs +156 -0
  63. package/vendor/krilla/src/error.rs +170 -0
  64. package/vendor/krilla/src/geom.rs +341 -0
  65. package/vendor/krilla/src/graphics/blend.rs +46 -0
  66. package/vendor/krilla/src/graphics/color.rs +552 -0
  67. package/vendor/krilla/src/graphics/graphic.rs +30 -0
  68. package/vendor/krilla/src/graphics/graphics_state.rs +251 -0
  69. package/vendor/krilla/src/graphics/icc.rs +218 -0
  70. package/vendor/krilla/src/graphics/image.rs +835 -0
  71. package/vendor/krilla/src/graphics/mask.rs +128 -0
  72. package/vendor/krilla/src/graphics/mod.rs +14 -0
  73. package/vendor/krilla/src/graphics/paint.rs +434 -0
  74. package/vendor/krilla/src/graphics/separation.rs +86 -0
  75. package/vendor/krilla/src/graphics/shading_function.rs +865 -0
  76. package/vendor/krilla/src/graphics/shading_pattern.rs +60 -0
  77. package/vendor/krilla/src/graphics/tiling_pattern.rs +119 -0
  78. package/vendor/krilla/src/graphics/xobject.rs +150 -0
  79. package/vendor/krilla/src/interactive/action.rs +68 -0
  80. package/vendor/krilla/src/interactive/annotation.rs +283 -0
  81. package/vendor/krilla/src/interactive/destination.rs +289 -0
  82. package/vendor/krilla/src/interactive/mod.rs +3 -0
  83. package/vendor/krilla/src/interchange/embed.rs +240 -0
  84. package/vendor/krilla/src/interchange/metadata.rs +564 -0
  85. package/vendor/krilla/src/interchange/mod.rs +4 -0
  86. package/vendor/krilla/src/interchange/outline.rs +263 -0
  87. package/vendor/krilla/src/interchange/tagging/fmt.rs +733 -0
  88. package/vendor/krilla/src/interchange/tagging/generate.toml +474 -0
  89. package/vendor/krilla/src/interchange/tagging/generated.rs +4754 -0
  90. package/vendor/krilla/src/interchange/tagging/mod.rs +1324 -0
  91. package/vendor/krilla/src/interchange/tagging/tag.rs +686 -0
  92. package/vendor/krilla/src/lib.rs +179 -0
  93. package/vendor/krilla/src/num.rs +26 -0
  94. package/vendor/krilla/src/page.rs +612 -0
  95. package/vendor/krilla/src/pdf.rs +313 -0
  96. package/vendor/krilla/src/resource.rs +436 -0
  97. package/vendor/krilla/src/serialize.rs +1101 -0
  98. package/vendor/krilla/src/stream.rs +368 -0
  99. package/vendor/krilla/src/surface.rs +664 -0
  100. package/vendor/krilla/src/text/cid.rs +488 -0
  101. package/vendor/krilla/src/text/font.rs +351 -0
  102. package/vendor/krilla/src/text/glyph/bitmap.rs +81 -0
  103. package/vendor/krilla/src/text/glyph/colr.rs +581 -0
  104. package/vendor/krilla/src/text/glyph/mod.rs +205 -0
  105. package/vendor/krilla/src/text/glyph/outline.rs +72 -0
  106. package/vendor/krilla/src/text/glyph/svg.rs +34 -0
  107. package/vendor/krilla/src/text/group.rs +463 -0
  108. package/vendor/krilla/src/text/mod.rs +232 -0
  109. package/vendor/krilla/src/text/shape.rs +102 -0
  110. package/vendor/krilla/src/text/type3.rs +514 -0
  111. package/vendor/krilla/src/util.rs +286 -0
  112. package/vendor/stylo/properties/longhands.toml +0 -1
  113. package/vendor/stylo/stylesheets/rule_parser.rs +1 -1
  114. package/wasm/blitz_pdf.js +10 -5
  115. package/wasm/blitz_pdf_bg.wasm +0 -0
@@ -0,0 +1,313 @@
1
+ //! Including other PDF files.
2
+
3
+ use std::collections::HashMap;
4
+ use std::fmt::{self, Debug, Display, Formatter};
5
+ use std::hash::{Hash, Hasher};
6
+ use std::ops::Deref;
7
+ use std::sync::{Arc, OnceLock};
8
+
9
+ use hayro_write::hayro_syntax::page::Page;
10
+ use hayro_write::hayro_syntax::PdfVersion as HayroPdfVersion;
11
+ use hayro_write::{ExtractionError, ExtractionQuery};
12
+ use pdf_writer::{Name, Ref};
13
+
14
+ pub use hayro_write::hayro_syntax::Pdf;
15
+
16
+ use crate::chunk_container::{ChunkContainer, EmbeddedPdfChunk};
17
+ use crate::configure::{PdfVersion, ValidationError};
18
+ use crate::error::{KrillaError, KrillaResult};
19
+ use crate::graphics::color::rgb;
20
+ use crate::resource::Resource;
21
+ use crate::serialize::{MaybeDeviceColorSpace, SerializeContext};
22
+ use crate::surface::Location;
23
+ use crate::util::{Deferred, Prehashed};
24
+
25
+ /// An error that can occur when embedding a PDF document.
26
+ #[derive(Debug, PartialEq, Eq, Clone)]
27
+ pub enum PdfError {
28
+ /// A page was requested that doesn't exist.
29
+ InvalidPage(usize),
30
+ /// The PDF version of the embedded PDF is not compatible with the version of the PDF
31
+ /// produced by krilla. This happens if the version of the krilla document is lower than
32
+ /// the one of the embedded PDF.
33
+ ///
34
+ /// The argument indicates the version of the embedded PDF document.
35
+ VersionMismatch(PdfVersion),
36
+ }
37
+
38
+ impl Display for PdfError {
39
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
40
+ match self {
41
+ PdfError::InvalidPage(index) => write!(f, "invalid page index {index}"),
42
+ PdfError::VersionMismatch(version) => write!(
43
+ f,
44
+ "embedded PDF requires {}, which is newer than the selected PDF version",
45
+ version.as_str()
46
+ ),
47
+ }
48
+ }
49
+ }
50
+
51
+ impl std::error::Error for PdfError {}
52
+
53
+ struct PdfDocumentRepr(Arc<Pdf>);
54
+
55
+ impl Debug for PdfDocumentRepr {
56
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57
+ write!(f, "PdfDocumentRepr {{ .. }}")
58
+ }
59
+ }
60
+
61
+ impl Hash for PdfDocumentRepr {
62
+ fn hash<H: Hasher>(&self, state: &mut H) {
63
+ self.0.data().as_ref().as_ref().hash(state);
64
+ }
65
+ }
66
+
67
+ /// An external PDF document.
68
+ #[derive(Clone, Hash, PartialEq, Eq, Debug)]
69
+ pub struct PdfDocument(Arc<Prehashed<PdfDocumentRepr>>);
70
+
71
+ impl PdfDocument {
72
+ /// Load a new PDF document.
73
+ pub fn new(pdf: Arc<Pdf>) -> PdfDocument {
74
+ Self(Arc::new(Prehashed::new(PdfDocumentRepr(pdf))))
75
+ }
76
+
77
+ pub(crate) fn pdf(&self) -> &Pdf {
78
+ &self.0.deref().0
79
+ }
80
+
81
+ pub(crate) fn pages(&self) -> &[Page<'_>] {
82
+ self.0.deref().0.pages()
83
+ }
84
+ }
85
+
86
+ #[derive(Default, Debug)]
87
+ pub(crate) struct PdfDocumentInfo {
88
+ counter: u64,
89
+ query_refs: Vec<Ref>,
90
+ cached_xobjects: HashMap<usize, Ref>,
91
+ queries: Vec<ExtractionQuery>,
92
+ locations: Vec<Option<Location>>,
93
+ }
94
+
95
+ impl PdfDocumentInfo {
96
+ pub(crate) fn new(counter: u64) -> Self {
97
+ Self {
98
+ counter,
99
+ ..Self::default()
100
+ }
101
+ }
102
+ }
103
+
104
+ #[derive(Default, Debug)]
105
+ pub(crate) struct PdfSerializerContext {
106
+ infos: HashMap<PdfDocument, PdfDocumentInfo>,
107
+ counter: u64,
108
+ }
109
+
110
+ impl PdfSerializerContext {
111
+ pub(crate) fn add_page(
112
+ &mut self,
113
+ document: &PdfDocument,
114
+ page_index: usize,
115
+ ref_: Ref,
116
+ location: Option<Location>,
117
+ ) {
118
+ // Unlike XObject's, we cannot cache page refs since Acrobat doesn't like duplicate page
119
+ // refs in the page tree.
120
+ let info = self.get_info(document);
121
+
122
+ info.query_refs.push(ref_);
123
+ info.queries.push(ExtractionQuery::new_page(page_index));
124
+ info.locations.push(location);
125
+ }
126
+
127
+ fn get_info(&mut self, document: &PdfDocument) -> &mut PdfDocumentInfo {
128
+ self.infos.entry(document.clone()).or_insert_with(|| {
129
+ let info = PdfDocumentInfo::new(self.counter);
130
+ self.counter += 1;
131
+
132
+ info
133
+ })
134
+ }
135
+
136
+ #[must_use = "This method might not use the original ref and could return a different one that \
137
+ was previously cached."]
138
+ pub(crate) fn add_xobject(
139
+ &mut self,
140
+ document: &PdfDocument,
141
+ page_index: usize,
142
+ ref_: Ref,
143
+ location: Option<Location>,
144
+ ) -> Ref {
145
+ let info = self.get_info(document);
146
+
147
+ if let Some(cached_ref) = info.cached_xobjects.get(&page_index) {
148
+ return *cached_ref;
149
+ }
150
+
151
+ info.query_refs.push(ref_);
152
+ info.queries.push(ExtractionQuery::new_xobject(page_index));
153
+ info.locations.push(location);
154
+
155
+ info.cached_xobjects.insert(page_index, ref_);
156
+
157
+ ref_
158
+ }
159
+
160
+ pub(crate) fn serialize(
161
+ self,
162
+ sc: &mut SerializeContext,
163
+ chunk_container: &mut ChunkContainer,
164
+ ) -> KrillaResult<()> {
165
+ let page_tree_parent_ref = sc.page_tree_ref();
166
+ let krilla_version = sc.serialize_settings().configuration.version();
167
+
168
+ let mut entries = self.infos.into_iter().collect::<Vec<_>>();
169
+ // Make sure we always process them in the same order.
170
+ entries.sort_by(|d1, d2| d1.1.counter.cmp(&d2.1.counter));
171
+
172
+ for (doc, info) in entries {
173
+ for location in info.locations.iter() {
174
+ sc.register_validation_error(ValidationError::EmbeddedPDF(*location))
175
+ }
176
+
177
+ // In theory `hayro-write` only requires this for PDFs that are embedded
178
+ // as XObject's to write the XObject group color space (embedded pages don't need it),
179
+ // but we just always allocate it if we have at least one PDF,
180
+ // regardless of how the files are embedded. We can change this in the
181
+ // future, but it keeps the code simpler.
182
+ let xobject_group_color_space = sc.register_colorspace(
183
+ chunk_container,
184
+ rgb::color_space(sc.serialize_settings().no_device_cs).into(),
185
+ );
186
+ let chunk_settings = sc.chunk_settings();
187
+
188
+ let deferred_chunk = Deferred::new(move || {
189
+ // We can't share the serializer context between threads, so each PDF has it's own
190
+ // reference, and we remap it later in `ChunkContainer`.
191
+ let mut new_ref = Ref::new(1);
192
+ // `local_ref` refers to the Ref that will be assigned to it locally
193
+ // in the extracted PDF chunk. Note that the colorspace itself
194
+ // doesn't actually exist in the extracted PDF, i.e. the Ref
195
+ // points to the null object. It just represents a placeholder Ref.
196
+ // Later on when actually merging the PDF chunk into the final
197
+ // krilla PDF, we replace `local_ref` with `cs_ref` such that it
198
+ // actually points to the colorspace that is embedded in the full PDF.
199
+ let (assigned_cs_ref, should_cs_ref) = match &xobject_group_color_space {
200
+ MaybeDeviceColorSpace::DeviceRgb => (None, None),
201
+ MaybeDeviceColorSpace::ColorSpace(cs) => {
202
+ (Some(new_ref.bump()), Some(cs.get_ref()))
203
+ }
204
+ MaybeDeviceColorSpace::DeviceGray | MaybeDeviceColorSpace::DeviceCMYK => {
205
+ unreachable!()
206
+ }
207
+ };
208
+
209
+ let first_location = info.locations.iter().flatten().next().cloned();
210
+ let pdf = doc.pdf();
211
+
212
+ let pdf_version = convert_pdf_version(pdf.version());
213
+
214
+ if krilla_version < pdf_version {
215
+ return Err(KrillaError::Pdf(
216
+ doc.clone(),
217
+ PdfError::VersionMismatch(pdf_version),
218
+ first_location,
219
+ ));
220
+ }
221
+
222
+ let extracted = hayro_write::extract(
223
+ pdf,
224
+ Box::new(|| new_ref.bump()),
225
+ chunk_settings,
226
+ |group| {
227
+ if let Some(local_group_color_space_ref) = assigned_cs_ref {
228
+ group
229
+ .insert(Name(b"CS"))
230
+ .primitive(local_group_color_space_ref);
231
+ } else {
232
+ // If we aren't using an ICC color space, just use
233
+ // the device RGB color space.
234
+ group.color_space().device_rgb();
235
+ }
236
+ },
237
+ &info.queries,
238
+ );
239
+ let result = convert_extraction_result(extracted, &doc, first_location.as_ref())?;
240
+
241
+ debug_assert_eq!(info.query_refs.len(), result.root_refs.len());
242
+
243
+ let mut root_ref_mappings = HashMap::new();
244
+
245
+ root_ref_mappings.insert(result.page_tree_parent_ref, page_tree_parent_ref);
246
+
247
+ if let (Some(local_group_color_space_ref), Some(group_color_space_ref)) =
248
+ (assigned_cs_ref, should_cs_ref)
249
+ {
250
+ root_ref_mappings.insert(local_group_color_space_ref, group_color_space_ref);
251
+ }
252
+
253
+ for ((should_ref, extraction_result), location) in info
254
+ .query_refs
255
+ .iter()
256
+ .zip(result.root_refs)
257
+ .zip(&info.locations)
258
+ {
259
+ let assigned_ref = convert_extraction_result(
260
+ extraction_result,
261
+ &doc,
262
+ location.clone().as_ref(),
263
+ )?;
264
+
265
+ root_ref_mappings.insert(assigned_ref, *should_ref);
266
+ }
267
+
268
+ Ok(EmbeddedPdfChunk {
269
+ root_ref_mappings,
270
+ original_chunk: result.chunk,
271
+ new_chunk: OnceLock::new(),
272
+ })
273
+ });
274
+
275
+ chunk_container.mixed.embedded_pdfs.push(deferred_chunk);
276
+ }
277
+
278
+ Ok(())
279
+ }
280
+ }
281
+
282
+ fn convert_pdf_version(version: HayroPdfVersion) -> PdfVersion {
283
+ match version {
284
+ // Those are obviously not right, but we don't support versions lower than 1.4 in krilla.
285
+ // Since we only need this conversion to detect version mismatches (in which case the
286
+ // version of the embedded PDF has to be higher than 1.4), this hack is sufficient for our
287
+ // purposes.
288
+ HayroPdfVersion::Pdf10 => PdfVersion::Pdf14,
289
+ HayroPdfVersion::Pdf11 => PdfVersion::Pdf14,
290
+ HayroPdfVersion::Pdf12 => PdfVersion::Pdf14,
291
+ HayroPdfVersion::Pdf13 => PdfVersion::Pdf14,
292
+
293
+ HayroPdfVersion::Pdf14 => PdfVersion::Pdf14,
294
+ HayroPdfVersion::Pdf15 => PdfVersion::Pdf15,
295
+ HayroPdfVersion::Pdf16 => PdfVersion::Pdf16,
296
+ HayroPdfVersion::Pdf17 => PdfVersion::Pdf17,
297
+ HayroPdfVersion::Pdf20 => PdfVersion::Pdf20,
298
+ }
299
+ }
300
+
301
+ fn convert_extraction_result<T>(
302
+ result: Result<T, ExtractionError>,
303
+ doc: &PdfDocument,
304
+ location: Option<&Location>,
305
+ ) -> KrillaResult<T> {
306
+ result.map_err(|e| {
307
+ let pdf_error = match e {
308
+ ExtractionError::InvalidPageIndex(i) => PdfError::InvalidPage(i),
309
+ };
310
+
311
+ KrillaError::Pdf(doc.clone(), pdf_error, location.cloned())
312
+ })
313
+ }