blitz-pdf 0.1.6 → 0.1.8

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 (88) hide show
  1. package/README.md +66 -11
  2. package/THIRD_PARTY_NOTICES.md +19 -1
  3. package/dist/api.d.ts +2 -1
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +31 -6
  6. package/dist/api.js.map +1 -1
  7. package/dist/browser.d.ts +2 -1
  8. package/dist/browser.d.ts.map +1 -1
  9. package/dist/browser.js +1 -1
  10. package/dist/browser.js.map +1 -1
  11. package/dist/node.d.ts +2 -1
  12. package/dist/node.d.ts.map +1 -1
  13. package/dist/node.js +6 -2
  14. package/dist/node.js.map +1 -1
  15. package/dist/types.d.ts +16 -1
  16. package/dist/types.d.ts.map +1 -1
  17. package/dist/worker-host.d.ts +10 -5
  18. package/dist/worker-host.d.ts.map +1 -1
  19. package/dist/worker-host.js +149 -28
  20. package/dist/worker-host.js.map +1 -1
  21. package/dist/worker-protocol.d.ts +5 -0
  22. package/dist/worker-protocol.d.ts.map +1 -1
  23. package/dist/worker-runtime.d.ts +1 -1
  24. package/dist/worker-runtime.d.ts.map +1 -1
  25. package/dist/worker-runtime.js +22 -6
  26. package/dist/worker-runtime.js.map +1 -1
  27. package/licenses/Noto-OFL.txt +94 -0
  28. package/licenses/NotoCJK-OFL.txt +92 -0
  29. package/licenses/NotoEmoji-OFL.txt +93 -0
  30. package/licenses/RUST_DEPENDENCIES.txt +34 -40
  31. package/licenses/Unifont-OFL.txt +90 -0
  32. package/licenses/font-sources.json +106 -0
  33. package/package.json +1 -1
  34. package/src/api.ts +32 -7
  35. package/src/browser.ts +2 -1
  36. package/src/node.ts +7 -2
  37. package/src/types.ts +17 -1
  38. package/src/worker-host.ts +148 -29
  39. package/src/worker-protocol.ts +5 -5
  40. package/src/worker-runtime.ts +20 -6
  41. package/vendor/README.md +38 -5
  42. package/vendor/blitz-dom/Cargo.toml +6 -6
  43. package/vendor/blitz-dom/src/accessibility.rs +175 -2
  44. package/vendor/blitz-dom/src/config.rs +8 -10
  45. package/vendor/blitz-dom/src/document.rs +66 -3
  46. package/vendor/blitz-dom/src/events/keyboard.rs +2 -6
  47. package/vendor/blitz-dom/src/layout/construct.rs +5 -300
  48. package/vendor/blitz-dom/src/layout/inline/build.rs +309 -0
  49. package/vendor/blitz-dom/src/layout/{inline.rs → inline/layout.rs} +1 -1
  50. package/vendor/blitz-dom/src/layout/inline/mod.rs +4 -0
  51. package/vendor/blitz-dom/src/layout/mod.rs +3 -1
  52. package/vendor/blitz-dom/src/layout/replaced.rs +13 -4
  53. package/vendor/blitz-dom/src/mutator.rs +47 -0
  54. package/vendor/blitz-dom/src/node/element.rs +17 -0
  55. package/vendor/blitz-dom/src/node/mod.rs +1 -0
  56. package/vendor/blitz-dom/src/node/node.rs +46 -5
  57. package/vendor/blitz-dom/src/node/text.rs +11 -11
  58. package/vendor/blitz-dom/src/resolve.rs +2 -2
  59. package/vendor/blitz-dom/src/stylo.rs +30 -22
  60. package/vendor/blitz-dom/src/util.rs +1 -1
  61. package/vendor/blitz-paint/Cargo.toml +4 -4
  62. package/vendor/fontique/Cargo.toml +194 -0
  63. package/vendor/fontique/LICENSE-APACHE +201 -0
  64. package/vendor/fontique/LICENSE-MIT +25 -0
  65. package/vendor/fontique/README.md +54 -0
  66. package/vendor/fontique/examples/generic_families.rs +37 -0
  67. package/vendor/fontique/src/attributes.rs +42 -0
  68. package/vendor/fontique/src/backend/android.rs +218 -0
  69. package/vendor/fontique/src/backend/coretext.rs +206 -0
  70. package/vendor/fontique/src/backend/dwrite.rs +395 -0
  71. package/vendor/fontique/src/backend/fontconfig.rs +844 -0
  72. package/vendor/fontique/src/backend/mod.rs +75 -0
  73. package/vendor/fontique/src/charmap.rs +127 -0
  74. package/vendor/fontique/src/collection/mod.rs +1016 -0
  75. package/vendor/fontique/src/collection/query.rs +392 -0
  76. package/vendor/fontique/src/fallback.rs +293 -0
  77. package/vendor/fontique/src/family.rs +111 -0
  78. package/vendor/fontique/src/family_name.rs +153 -0
  79. package/vendor/fontique/src/font.rs +562 -0
  80. package/vendor/fontique/src/generic.rs +36 -0
  81. package/vendor/fontique/src/impl_fontconfig.rs +159 -0
  82. package/vendor/fontique/src/lib.rs +87 -0
  83. package/vendor/fontique/src/matching.rs +483 -0
  84. package/vendor/fontique/src/scan.rs +312 -0
  85. package/vendor/fontique/src/script.rs +271 -0
  86. package/vendor/fontique/src/source.rs +108 -0
  87. package/vendor/fontique/src/source_cache.rs +254 -0
  88. package/wasm/blitz_pdf_bg.wasm +0 -0
@@ -0,0 +1,108 @@
1
+ // Copyright 2024 the Parley Authors
2
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
3
+
4
+ //! Model for font data.
5
+
6
+ use crate::AtomicCounter;
7
+ use core::sync::atomic::Ordering;
8
+ use linebender_resource_handle::Blob;
9
+ #[cfg(feature = "std")]
10
+ use {
11
+ hashbrown::HashMap,
12
+ std::{path::Path, sync::Arc},
13
+ };
14
+
15
+ /// Unique identifier for a font source.
16
+ ///
17
+ /// See [`SourceInfo`].
18
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
19
+ #[repr(transparent)]
20
+ pub struct SourceId(u64);
21
+
22
+ impl SourceId {
23
+ /// Creates a new unique identifier.
24
+ #[allow(clippy::new_without_default)]
25
+ pub fn new() -> Self {
26
+ static ID_COUNTER: AtomicCounter = AtomicCounter::new(1);
27
+ #[allow(clippy::useless_conversion, reason = "Not useless on 32-bit platforms")]
28
+ Self(ID_COUNTER.fetch_add(1, Ordering::Relaxed).into())
29
+ }
30
+
31
+ /// Returns the underlying integer value.
32
+ pub fn to_u64(self) -> u64 {
33
+ self.0
34
+ }
35
+ }
36
+
37
+ /// Handle that associates font data with a unique identifier.
38
+ #[derive(Clone, Debug)]
39
+ pub struct SourceInfo {
40
+ pub id: SourceId,
41
+ pub kind: SourceKind,
42
+ }
43
+
44
+ impl SourceInfo {
45
+ /// Creates a new source with the given identifier and kind.
46
+ pub fn new(id: SourceId, kind: SourceKind) -> Self {
47
+ Self { id, kind }
48
+ }
49
+
50
+ /// Returns the unique identifier for the source.
51
+ pub fn id(&self) -> SourceId {
52
+ self.id
53
+ }
54
+
55
+ /// Returns the kind of the source.
56
+ pub fn kind(&self) -> &SourceKind {
57
+ &self.kind
58
+ }
59
+ }
60
+
61
+ /// Font data that is either a path to the font file or shared data in memory.
62
+ ///
63
+ /// See [`SourceInfo`].
64
+ #[derive(Clone)]
65
+ pub enum SourceKind {
66
+ /// Shared data containing the content of the font file.
67
+ Memory(Blob<u8>),
68
+ #[cfg(feature = "std")]
69
+ /// Path to a font file.
70
+ Path(Arc<Path>),
71
+ }
72
+
73
+ impl core::fmt::Debug for SourceKind {
74
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75
+ match self {
76
+ #[cfg(feature = "std")]
77
+ Self::Path(path) => {
78
+ write!(f, "Path({path:?})")
79
+ }
80
+ Self::Memory(_) => {
81
+ write!(f, "Data([..])")
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+ #[cfg(feature = "std")]
88
+ /// Map for deduplicating font data file paths.
89
+ #[derive(Default)]
90
+ pub struct SourcePathMap {
91
+ map: HashMap<Arc<Path>, SourceInfo>,
92
+ }
93
+
94
+ #[cfg(feature = "std")]
95
+ impl SourcePathMap {
96
+ /// Converts a path string into a font data object, creating it if it
97
+ /// doesn't already exist.
98
+ pub fn get_or_insert(&mut self, path: &Path) -> SourceInfo {
99
+ if let Some(source) = self.map.get(path) {
100
+ source.clone()
101
+ } else {
102
+ let path: Arc<Path> = path.into();
103
+ let source = SourceInfo::new(SourceId::new(), SourceKind::Path(path.clone()));
104
+ self.map.insert(path, source.clone());
105
+ source
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,254 @@
1
+ // Copyright 2024 the Parley Authors
2
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
3
+
4
+ //! Cache for font data.
5
+
6
+ #[cfg(feature = "std")]
7
+ use super::source::SourceId;
8
+ use super::source::{SourceInfo, SourceKind};
9
+ #[cfg(feature = "std")]
10
+ use hashbrown::HashMap;
11
+ use linebender_resource_handle::Blob;
12
+ #[cfg(feature = "std")]
13
+ use linebender_resource_handle::WeakBlob;
14
+ #[cfg(feature = "std")]
15
+ use std::{
16
+ path::Path,
17
+ sync::{Arc, Mutex},
18
+ };
19
+
20
+ /// Options for a [source cache].
21
+ ///
22
+ /// [source cache]: SourceCache
23
+ #[derive(Copy, Clone, Default, Debug)]
24
+ pub struct SourceCacheOptions {
25
+ #[cfg(feature = "std")]
26
+ /// If true, the source cache will use a secondary shared cache
27
+ /// guaranteeing that all clones will use the same backing store.
28
+ ///
29
+ /// This is useful for ensuring that only one copy of font data is
30
+ /// loaded into memory in multi-threaded scenarios.
31
+ ///
32
+ /// The default value is `false`.
33
+ pub shared: bool,
34
+ }
35
+
36
+ /// Cache for font data loaded from the file system.
37
+ #[derive(Clone, Default)]
38
+ pub struct SourceCache {
39
+ #[cfg(feature = "std")]
40
+ cache: HashMap<SourceId, Entry<Blob<u8>>>,
41
+ #[cfg(feature = "std")]
42
+ serial: u64,
43
+ #[cfg(feature = "std")]
44
+ shared: Option<Arc<Mutex<Shared>>>,
45
+ }
46
+
47
+ impl SourceCache {
48
+ /// Creates an empty cache with the given [options].
49
+ ///
50
+ /// [options]: SourceCacheOptions
51
+ #[cfg_attr(not(feature = "std"), allow(unused))]
52
+ pub fn new(options: SourceCacheOptions) -> Self {
53
+ #[cfg(feature = "std")]
54
+ if options.shared {
55
+ return Self {
56
+ cache: HashMap::default(),
57
+ serial: 0,
58
+ shared: Some(Arc::new(Mutex::new(Shared::default()))),
59
+ };
60
+ }
61
+ Self::default()
62
+ }
63
+
64
+ /// Creates an empty cache that is suitable for multi-threaded use.
65
+ ///
66
+ /// A cache created with this function maintains a synchronized internal
67
+ /// store that is shared among all clones.
68
+ ///
69
+ /// This is the same as calling [`SourceCache::new`] with an options
70
+ /// struct where `shared = true`.
71
+ #[cfg(feature = "std")]
72
+ pub fn new_shared() -> Self {
73
+ Self {
74
+ cache: HashMap::default(),
75
+ serial: 0,
76
+ shared: Some(Arc::new(Mutex::new(Shared::default()))),
77
+ }
78
+ }
79
+
80
+ /// Turns an unshared cache into a shared cache that can used to ensure that fonts only get loaded once
81
+ /// even when they are loaded across multiple threads.
82
+ #[cfg(feature = "std")]
83
+ pub fn make_shared(&mut self) {
84
+ if self.shared.is_none() {
85
+ self.shared = Some(Arc::new(Mutex::new(Shared::from_local(&self.cache))));
86
+ }
87
+ }
88
+
89
+ /// Returns the [blob] for the given font data, attempting to load
90
+ /// it from the file system if not already present.
91
+ ///
92
+ /// Returns `None` if loading failed.
93
+ ///
94
+ /// [blob]: Blob
95
+ pub fn get(&mut self, source: &SourceInfo) -> Option<Blob<u8>> {
96
+ match &source.kind {
97
+ SourceKind::Memory(memory) => Some(memory.clone()),
98
+ #[cfg(feature = "std")]
99
+ SourceKind::Path(path) => {
100
+ use hashbrown::hash_map::Entry as HashEntry;
101
+ match self.cache.entry(source.id()) {
102
+ HashEntry::Vacant(vacant) => {
103
+ if let Some(mut shared) =
104
+ self.shared.as_ref().and_then(|shared| shared.lock().ok())
105
+ {
106
+ // If we have a backing cache, try to load it there first
107
+ // and then propagate the result here.
108
+ if let Some(blob) = shared.get(source.id(), path) {
109
+ vacant.insert(Entry::Loaded(EntryData {
110
+ font_data: blob.clone(),
111
+ serial: self.serial,
112
+ }));
113
+ Some(blob)
114
+ } else {
115
+ vacant.insert(Entry::Failed);
116
+ None
117
+ }
118
+ } else {
119
+ // Otherwise, load it ourselves.
120
+ if let Some(blob) = load_blob(path) {
121
+ vacant.insert(Entry::Loaded(EntryData {
122
+ font_data: blob.clone(),
123
+ serial: self.serial,
124
+ }));
125
+ Some(blob)
126
+ } else {
127
+ vacant.insert(Entry::Failed);
128
+ None
129
+ }
130
+ }
131
+ }
132
+ HashEntry::Occupied(mut occupied) => {
133
+ let entry = occupied.get_mut();
134
+ match entry {
135
+ Entry::Loaded(data) => {
136
+ data.serial = self.serial;
137
+ Some(data.font_data.clone())
138
+ }
139
+ Entry::Failed => None,
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ /// Removes all cached blobs that have not been accessed in the last
148
+ /// `max_age` times `prune` has been called.
149
+ #[cfg_attr(not(feature = "std"), allow(unused))]
150
+ pub fn prune(&mut self, max_age: u64, prune_failed: bool) {
151
+ #[cfg(feature = "std")]
152
+ {
153
+ self.cache.retain(|_, entry| match entry {
154
+ Entry::Failed => !prune_failed,
155
+ Entry::Loaded(data) => self.serial.saturating_sub(data.serial) < max_age,
156
+ });
157
+ self.serial = self.serial.saturating_add(1);
158
+ }
159
+ }
160
+ }
161
+
162
+ /// Shared backing store for a font data cache.
163
+ #[cfg(feature = "std")]
164
+ #[derive(Default)]
165
+ struct Shared {
166
+ cache: HashMap<SourceId, Entry<WeakBlob<u8>>>,
167
+ }
168
+
169
+ #[cfg(feature = "std")]
170
+ impl Shared {
171
+ /// Bootstrap a shared cache from a local one
172
+ fn from_local(unshared: &HashMap<SourceId, Entry<Blob<u8>>>) -> Self {
173
+ let shared_cache: HashMap<SourceId, Entry<WeakBlob<u8>>> = unshared
174
+ .iter()
175
+ .map(|(key, value)| (*key, value.into()))
176
+ .collect();
177
+ Self {
178
+ cache: shared_cache,
179
+ }
180
+ }
181
+
182
+ pub fn get(&mut self, id: SourceId, path: &Path) -> Option<Blob<u8>> {
183
+ use hashbrown::hash_map::Entry as HashEntry;
184
+ match self.cache.entry(id) {
185
+ HashEntry::Vacant(vacant) => {
186
+ if let Some(blob) = load_blob(path) {
187
+ vacant.insert(Entry::Loaded(EntryData {
188
+ font_data: blob.downgrade(),
189
+ serial: 0,
190
+ }));
191
+ Some(blob)
192
+ } else {
193
+ vacant.insert(Entry::Failed);
194
+ None
195
+ }
196
+ }
197
+ HashEntry::Occupied(mut occupied) => {
198
+ let entry = occupied.get_mut();
199
+ match entry {
200
+ Entry::Loaded(data) => {
201
+ if let Some(blob) = data.font_data.upgrade() {
202
+ // The weak ref is still valid.
203
+ Some(blob)
204
+ } else if let Some(blob) = load_blob(path) {
205
+ // Otherwise, try to reload it.
206
+ data.font_data = blob.downgrade();
207
+ Some(blob)
208
+ } else {
209
+ // We failed for some reason.. don't try again.
210
+ *entry = Entry::Failed;
211
+ None
212
+ }
213
+ }
214
+ Entry::Failed => None,
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ #[cfg(feature = "std")]
222
+ #[derive(Clone, Default)]
223
+ enum Entry<T> {
224
+ Loaded(EntryData<T>),
225
+ #[default]
226
+ Failed,
227
+ }
228
+
229
+ #[cfg(feature = "std")]
230
+ #[derive(Clone)]
231
+ struct EntryData<T> {
232
+ font_data: T,
233
+ serial: u64,
234
+ }
235
+
236
+ #[cfg(feature = "std")]
237
+ impl<T> From<&Entry<Blob<T>>> for Entry<WeakBlob<T>> {
238
+ fn from(value: &Entry<Blob<T>>) -> Self {
239
+ match value {
240
+ Entry::Loaded(entry_data) => Self::Loaded(EntryData {
241
+ font_data: entry_data.font_data.downgrade(),
242
+ serial: entry_data.serial,
243
+ }),
244
+ Entry::Failed => Self::Failed,
245
+ }
246
+ }
247
+ }
248
+
249
+ #[cfg(feature = "std")]
250
+ pub(crate) fn load_blob(path: &Path) -> Option<Blob<u8>> {
251
+ let file = std::fs::File::open(path).ok()?;
252
+ let mapped = unsafe { memmap2::Mmap::map(&file).ok()? };
253
+ Some(Blob::new(Arc::new(mapped)))
254
+ }
Binary file