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,251 @@
1
+ use std::hash::Hash;
2
+ use std::sync::Arc;
3
+
4
+ use pdf_writer::types::BlendMode;
5
+ use pdf_writer::{Finish, Name, Ref};
6
+
7
+ use crate::chunk_container::ChunkContainer;
8
+ use crate::configure::ValidationError;
9
+ use crate::geom::{Rect, Transform};
10
+ use crate::graphics::mask::Mask;
11
+ use crate::num::NormalizedF32;
12
+ use crate::resource;
13
+ use crate::resource::Resourceable;
14
+ use crate::serialize::{Cacheable, SerializeContext};
15
+
16
+ /// The inner representation of an external graphics state.
17
+ #[derive(Debug, Hash, PartialEq, Eq, Default, Clone)]
18
+ struct Repr {
19
+ /// The non-stroking alpha.
20
+ non_stroking_alpha: Option<NormalizedF32>,
21
+ /// The stroking alpha.
22
+ stroking_alpha: Option<NormalizedF32>,
23
+ /// The blend mode.
24
+ blend_mode: Option<BlendMode>,
25
+ /// An active mask.
26
+ mask: Option<Ref>,
27
+ }
28
+
29
+ /// A graphics state containing information about
30
+ /// - The current stroking alpha.
31
+ /// - The current non-stroking alpha.
32
+ /// - The current blend mode.
33
+ /// - The current mask.
34
+ ///
35
+ /// This struct provides exposes a builder pattern for setting the various properties
36
+ /// individually.
37
+ ///
38
+ /// This type is cheap to clone.
39
+ #[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
40
+ pub(crate) struct ExtGState(Arc<Repr>);
41
+
42
+ impl ExtGState {
43
+ /// Create a new, empty graphics state.
44
+ pub(crate) fn new() -> Self {
45
+ Self::default()
46
+ }
47
+
48
+ /// Create a new graphics state with a stroking alpha.
49
+ #[must_use]
50
+ pub(crate) fn stroking_alpha(mut self, stroking_alpha: NormalizedF32) -> Self {
51
+ Arc::make_mut(&mut self.0).stroking_alpha = Some(stroking_alpha);
52
+ self
53
+ }
54
+
55
+ /// Create a new graphics state with a non-stroking alpha.
56
+ #[must_use]
57
+ pub(crate) fn non_stroking_alpha(mut self, non_stroking_alpha: NormalizedF32) -> Self {
58
+ Arc::make_mut(&mut self.0).non_stroking_alpha = Some(non_stroking_alpha);
59
+ self
60
+ }
61
+
62
+ /// Create a new graphics state with a blend mode.
63
+ #[must_use]
64
+ pub(crate) fn blend_mode(mut self, blend_mode: BlendMode) -> Self {
65
+ Arc::make_mut(&mut self.0).blend_mode = Some(blend_mode);
66
+ self
67
+ }
68
+
69
+ /// Create a new graphics state with a mask.
70
+ #[must_use]
71
+ pub(crate) fn mask(
72
+ mut self,
73
+ mask: Mask,
74
+ sc: &mut SerializeContext,
75
+ chunk_container: &mut ChunkContainer,
76
+ ) -> Self {
77
+ let mask_ref = sc.register_cacheable(chunk_container, mask);
78
+ Arc::make_mut(&mut self.0).mask = Some(mask_ref);
79
+ self
80
+ }
81
+
82
+ /// Check whether the graphics state is empty.
83
+ pub(crate) fn empty(&self) -> bool {
84
+ self.0.mask.is_none()
85
+ && self.0.stroking_alpha.is_none()
86
+ && self.0.non_stroking_alpha.is_none()
87
+ && self.0.blend_mode.is_none()
88
+ }
89
+
90
+ /// Integrate another graphics state into the current one. This is done by replacing
91
+ /// all active properties of the other graphics state in the current graphics state, while
92
+ /// leaving the inactive ones unchanged.
93
+ pub(crate) fn combine(&mut self, other: &ExtGState) {
94
+ if let Some(stroking_alpha) = other.0.stroking_alpha {
95
+ Arc::make_mut(&mut self.0).stroking_alpha = Some(stroking_alpha);
96
+ }
97
+
98
+ if let Some(non_stroking_alpha) = other.0.non_stroking_alpha {
99
+ Arc::make_mut(&mut self.0).non_stroking_alpha = Some(non_stroking_alpha);
100
+ }
101
+
102
+ if let Some(blend_mode) = other.0.blend_mode {
103
+ Arc::make_mut(&mut self.0).blend_mode = Some(blend_mode);
104
+ }
105
+
106
+ if let Some(mask) = other.0.mask {
107
+ Arc::make_mut(&mut self.0).mask = Some(mask);
108
+ }
109
+ }
110
+ }
111
+
112
+ impl Cacheable for ExtGState {
113
+ fn serialize(
114
+ self,
115
+ sc: &mut SerializeContext,
116
+ chunk_container: &mut ChunkContainer,
117
+ root_ref: Ref,
118
+ ) {
119
+ let chunk = &mut chunk_container.non_stream.ext_g_states;
120
+
121
+ let mut ext_st = chunk.ext_graphics(root_ref);
122
+ if let Some(nsa) = self.0.non_stroking_alpha {
123
+ if nsa != NormalizedF32::ONE {
124
+ sc.register_validation_error(ValidationError::Transparency(sc.location));
125
+ }
126
+
127
+ ext_st.non_stroking_alpha(nsa.get());
128
+ }
129
+
130
+ if let Some(sa) = self.0.stroking_alpha {
131
+ if sa != NormalizedF32::ONE {
132
+ sc.register_validation_error(ValidationError::Transparency(sc.location));
133
+ }
134
+
135
+ ext_st.stroking_alpha(sa.get());
136
+ }
137
+
138
+ if let Some(bm) = self.0.blend_mode {
139
+ if bm != BlendMode::Normal {
140
+ sc.register_validation_error(ValidationError::Transparency(sc.location));
141
+ }
142
+
143
+ ext_st.blend_mode(bm);
144
+ }
145
+
146
+ if let Some(mask_ref) = self.0.mask {
147
+ sc.register_validation_error(ValidationError::Transparency(sc.location));
148
+
149
+ ext_st.pair(Name(b"SMask"), mask_ref);
150
+ }
151
+
152
+ ext_st.finish();
153
+ }
154
+ }
155
+
156
+ impl Resourceable for ExtGState {
157
+ type Resource = resource::ExtGState;
158
+ }
159
+
160
+ /// A simulation of the PDF graphics state, so that we
161
+ /// can write our transforms/graphics state all at once
162
+ /// when adding an image/path instead of having to
163
+ /// use `save_state`/`restore_state` excessively.
164
+ #[derive(Debug, PartialEq, Clone)]
165
+ pub(crate) struct GraphicsState {
166
+ ext_g_state: ExtGState,
167
+ ctm: Transform,
168
+ }
169
+ impl Eq for GraphicsState {}
170
+
171
+ impl Hash for GraphicsState {
172
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
173
+ self.ext_g_state.hash(state);
174
+ self.ctm.hash(state);
175
+ }
176
+ }
177
+
178
+ impl Default for GraphicsState {
179
+ fn default() -> Self {
180
+ Self {
181
+ ext_g_state: ExtGState::new(),
182
+ ctm: Transform::identity(),
183
+ }
184
+ }
185
+ }
186
+
187
+ impl GraphicsState {
188
+ pub(crate) fn combine(&mut self, other: &ExtGState) {
189
+ self.ext_g_state.combine(other);
190
+ }
191
+
192
+ pub(crate) fn concat_transform(&mut self, transform: Transform) {
193
+ self.ctm = self.ctm.pre_concat(transform);
194
+ }
195
+
196
+ pub(crate) fn transform(&self) -> Transform {
197
+ self.ctm
198
+ }
199
+
200
+ pub(crate) fn ext_g_state(&self) -> &ExtGState {
201
+ &self.ext_g_state
202
+ }
203
+ }
204
+
205
+ /// A collection of graphics states, simulates the stack-like
206
+ /// structure used.
207
+ #[derive(Debug, Hash, Eq, PartialEq)]
208
+ pub struct GraphicsStates {
209
+ graphics_states: Vec<GraphicsState>,
210
+ }
211
+
212
+ impl GraphicsStates {
213
+ pub(crate) fn new() -> Self {
214
+ GraphicsStates {
215
+ graphics_states: vec![GraphicsState::default()],
216
+ }
217
+ }
218
+
219
+ pub(crate) fn cur(&self) -> &GraphicsState {
220
+ self.graphics_states.last().unwrap()
221
+ }
222
+
223
+ pub(crate) fn cur_mut(&mut self) -> &mut GraphicsState {
224
+ self.graphics_states.last_mut().unwrap()
225
+ }
226
+
227
+ pub(crate) fn save_state(&mut self) {
228
+ let state = self.cur();
229
+ self.graphics_states.push(state.clone())
230
+ }
231
+
232
+ pub(crate) fn restore_state(&mut self) {
233
+ self.graphics_states.pop();
234
+ }
235
+
236
+ pub(crate) fn combine(&mut self, other: &ExtGState) {
237
+ self.cur_mut().combine(other);
238
+ }
239
+
240
+ pub(crate) fn transform(&mut self, transform: Transform) {
241
+ self.cur_mut().concat_transform(transform);
242
+ }
243
+
244
+ pub(crate) fn transform_bbox(&self, bbox: Rect) -> Rect {
245
+ // Important: This does not take the root transform of the
246
+ // corresponding ContentBuilder into account, because we
247
+ // want it to be in krilla coordinates, not in PDF
248
+ // coordinates.
249
+ bbox.transform(self.cur().transform()).unwrap()
250
+ }
251
+ }
@@ -0,0 +1,218 @@
1
+ //! Using ICC profiles.
2
+
3
+ use std::ops::Deref;
4
+ use std::ops::DerefMut;
5
+ use std::sync::Arc;
6
+
7
+ use pdf_writer::{Finish, Name, Ref};
8
+
9
+ use crate::chunk_container::ChunkContainer;
10
+ use crate::resource;
11
+ use crate::resource::Resourceable;
12
+ use crate::serialize::{Cacheable, SerializeContext};
13
+ use crate::stream::{deflate_encode, FilterStreamBuilder};
14
+ use crate::util::Prehashed;
15
+
16
+ /// An ICC profile.
17
+ #[derive(Debug, Eq, PartialEq, Hash, Clone)]
18
+ pub struct ICCProfile<const C: u8>(Arc<Prehashed<Repr>>);
19
+
20
+ impl<const C: u8> ICCProfile<C> {
21
+ /// Create a new ICC profile.
22
+ ///
23
+ /// Returns `None` if the metadata of the profile couldn't be read or if the
24
+ /// number of channels of the underlying data does not correspond to the one
25
+ /// indicated in the constant parameter.
26
+ pub fn new(data: &[u8]) -> Option<Self> {
27
+ let metadata = ICCMetadata::from_data(data)?;
28
+
29
+ if metadata.color_space.num_components() != C {
30
+ return None;
31
+ }
32
+
33
+ Some(Self(Arc::new(Prehashed::new(Repr {
34
+ data: deflate_encode(data),
35
+ metadata,
36
+ }))))
37
+ }
38
+
39
+ pub(crate) fn metadata(&self) -> &ICCMetadata {
40
+ &self.0.metadata
41
+ }
42
+ }
43
+
44
+ impl<const C: u8> Cacheable for ICCProfile<C> {
45
+ fn serialize(
46
+ self,
47
+ sc: &mut SerializeContext,
48
+ chunk_container: &mut ChunkContainer,
49
+ root_ref: Ref,
50
+ ) {
51
+ let mut chunk = sc.new_chunk();
52
+ let icc_stream = FilterStreamBuilder::new_from_deflated(&self.0.deref().data)
53
+ .finish(&sc.serialize_settings());
54
+
55
+ let mut icc_profile = chunk.icc_profile(root_ref, icc_stream.encoded_data());
56
+ icc_profile.n(C as i32).range([0.0, 1.0].repeat(C as usize));
57
+ icc_stream.write_filters(icc_profile.deref_mut().deref_mut());
58
+ icc_profile.finish();
59
+ chunk_container.streams.icc_profiles.push(chunk);
60
+ }
61
+ }
62
+
63
+ #[cfg(feature = "raster-images")]
64
+ #[derive(Debug, Eq, PartialEq, Hash, Clone)]
65
+ pub(crate) enum GenericICCProfile {
66
+ Luma(ICCProfile<1>),
67
+ Rgb(ICCProfile<3>),
68
+ Cmyk(ICCProfile<4>),
69
+ }
70
+
71
+ #[cfg(feature = "raster-images")]
72
+ impl GenericICCProfile {
73
+ pub(crate) fn metadata(&self) -> &ICCMetadata {
74
+ match self {
75
+ GenericICCProfile::Luma(l) => l.metadata(),
76
+ GenericICCProfile::Rgb(r) => r.metadata(),
77
+ GenericICCProfile::Cmyk(c) => c.metadata(),
78
+ }
79
+ }
80
+ }
81
+
82
+ #[cfg(feature = "raster-images")]
83
+ impl Cacheable for GenericICCProfile {
84
+ fn serialize(
85
+ self,
86
+ sc: &mut SerializeContext,
87
+ chunk_container: &mut ChunkContainer,
88
+ root_ref: Ref,
89
+ ) {
90
+ match self {
91
+ GenericICCProfile::Luma(l) => l.serialize(sc, chunk_container, root_ref),
92
+ GenericICCProfile::Rgb(r) => r.serialize(sc, chunk_container, root_ref),
93
+ GenericICCProfile::Cmyk(c) => c.serialize(sc, chunk_container, root_ref),
94
+ }
95
+ }
96
+ }
97
+
98
+ #[derive(Debug, Eq, PartialEq, Hash, Clone)]
99
+ pub(crate) struct ICCBasedColorSpace<const C: u8>(pub(crate) ICCProfile<C>);
100
+
101
+ impl<const C: u8> Cacheable for ICCBasedColorSpace<C> {
102
+ fn serialize(
103
+ self,
104
+ sc: &mut SerializeContext,
105
+ chunk_container: &mut ChunkContainer,
106
+ root_ref: Ref,
107
+ ) {
108
+ let icc_ref = sc.register_cacheable(chunk_container, self.0.clone());
109
+
110
+ let chunk = &mut chunk_container.non_stream.color_spaces;
111
+
112
+ let mut array = chunk.indirect(root_ref).array();
113
+ array.item(Name(b"ICCBased"));
114
+ array.item(icc_ref);
115
+ array.finish();
116
+ }
117
+ }
118
+
119
+ impl<const C: u8> Resourceable for ICCBasedColorSpace<C> {
120
+ type Resource = resource::ColorSpace;
121
+ }
122
+
123
+ #[derive(Clone, Hash, Debug, Eq, PartialEq)]
124
+ pub(crate) enum ICCColorSpace {
125
+ Xyz,
126
+ Lab,
127
+ Luv,
128
+ Ycbr,
129
+ Yxy,
130
+ Lms,
131
+ Rgb,
132
+ Gray,
133
+ Hsv,
134
+ Hls,
135
+ Cmyk,
136
+ Cmy,
137
+ OneClr,
138
+ ThreeClr,
139
+ FourClr,
140
+ // There are more, but those should be the most important
141
+ // ones.
142
+ }
143
+
144
+ impl ICCColorSpace {
145
+ pub(crate) fn num_components(&self) -> u8 {
146
+ match self {
147
+ ICCColorSpace::Xyz => 3,
148
+ ICCColorSpace::Lab => 3,
149
+ ICCColorSpace::Luv => 3,
150
+ ICCColorSpace::Ycbr => 3,
151
+ ICCColorSpace::Yxy => 3,
152
+ ICCColorSpace::Lms => 3,
153
+ ICCColorSpace::Rgb => 3,
154
+ ICCColorSpace::Gray => 1,
155
+ ICCColorSpace::Hsv => 3,
156
+ ICCColorSpace::Hls => 3,
157
+ ICCColorSpace::Cmyk => 4,
158
+ ICCColorSpace::Cmy => 3,
159
+ ICCColorSpace::OneClr => 1,
160
+ ICCColorSpace::ThreeClr => 3,
161
+ ICCColorSpace::FourClr => 4,
162
+ }
163
+ }
164
+ }
165
+
166
+ impl TryFrom<u32> for ICCColorSpace {
167
+ type Error = ();
168
+
169
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
170
+ match value {
171
+ 0x58595A20 => Ok(ICCColorSpace::Xyz),
172
+ 0x4C616220 => Ok(ICCColorSpace::Lab),
173
+ 0x4C757620 => Ok(ICCColorSpace::Luv),
174
+ 0x59436272 => Ok(ICCColorSpace::Ycbr),
175
+ 0x59787920 => Ok(ICCColorSpace::Yxy),
176
+ 0x4C4D5320 => Ok(ICCColorSpace::Lms),
177
+ 0x52474220 => Ok(ICCColorSpace::Rgb),
178
+ 0x47524159 => Ok(ICCColorSpace::Gray),
179
+ 0x48535620 => Ok(ICCColorSpace::Hsv),
180
+ 0x484C5320 => Ok(ICCColorSpace::Hls),
181
+ 0x434D594B => Ok(ICCColorSpace::Cmyk),
182
+ 0x434D5920 => Ok(ICCColorSpace::Cmy),
183
+ 0x31434C52 => Ok(ICCColorSpace::OneClr),
184
+ 0x33434C52 => Ok(ICCColorSpace::ThreeClr),
185
+ 0x34434C52 => Ok(ICCColorSpace::FourClr),
186
+ _ => Err(()),
187
+ }
188
+ }
189
+ }
190
+
191
+ #[derive(Clone, Hash, Debug, Eq, PartialEq)]
192
+ pub(crate) struct ICCMetadata {
193
+ pub(crate) major: u8,
194
+ pub(crate) minor: u8,
195
+ pub(crate) color_space: ICCColorSpace,
196
+ }
197
+
198
+ impl ICCMetadata {
199
+ pub(crate) fn from_data(data: &[u8]) -> Option<Self> {
200
+ let major = *data.get(8)?;
201
+ let minor = *data.get(9)? >> 4;
202
+ let color_space = {
203
+ let marker = u32::from_be_bytes(data.get(16..20)?.try_into().ok()?);
204
+ ICCColorSpace::try_from(marker).ok()?
205
+ };
206
+ Some(Self {
207
+ major,
208
+ minor,
209
+ color_space,
210
+ })
211
+ }
212
+ }
213
+
214
+ #[derive(Clone, Hash, Debug)]
215
+ struct Repr {
216
+ data: Vec<u8>,
217
+ metadata: ICCMetadata,
218
+ }