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,1016 @@
1
+ // Copyright 2024 the Parley Authors
2
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
3
+
4
+ //! Support for working with font collections.
5
+
6
+ mod query;
7
+
8
+ pub use query::{Query, QueryFamily, QueryFont, QueryStatus};
9
+
10
+ use crate::font::FontInfoOverride;
11
+
12
+ use super::SourceCache;
13
+
14
+ use super::{
15
+ Blob, FontStyle, FontWeight, FontWidth, GenericFamily, Language, Script,
16
+ backend::SystemFonts,
17
+ fallback::{FallbackKey, FallbackMap},
18
+ family::{FamilyId, FamilyInfo},
19
+ family_name::{FamilyName, FamilyNameMap},
20
+ font::FontInfo,
21
+ generic::GenericFamilyMap,
22
+ source::{SourceId, SourceInfo, SourceKind},
23
+ };
24
+ use crate::{AtomicCounter, CounterInt};
25
+ use alloc::{string::String, sync::Arc, vec::Vec};
26
+ use hashbrown::HashMap;
27
+ use read_fonts::types::NameId;
28
+ #[cfg(feature = "std")]
29
+ use std::path::Path;
30
+ #[cfg(feature = "std")]
31
+ use std::sync::{Mutex, atomic::Ordering};
32
+
33
+ type FamilyMap = HashMap<FamilyId, Option<FamilyInfo>>;
34
+
35
+ /// Options for a [font collection](Collection).
36
+ #[derive(Copy, Clone, Debug)]
37
+ pub struct CollectionOptions {
38
+ /// If true, the font collection will use a secondary shared store
39
+ /// guaranteeing that any changes to the collection will be
40
+ /// visible to all clones.
41
+ ///
42
+ /// If the font collection will be used by a single thread, this is
43
+ /// pure overhead and should be disabled.
44
+ ///
45
+ /// The default value is `false`.
46
+ pub shared: bool,
47
+
48
+ /// If true, the font collection will provide access to system fonts
49
+ /// using platform specific APIs.
50
+ ///
51
+ /// The default value is `true`.
52
+ pub system_fonts: bool,
53
+ }
54
+
55
+ impl Default for CollectionOptions {
56
+ fn default() -> Self {
57
+ Self {
58
+ shared: false,
59
+ system_fonts: true,
60
+ }
61
+ }
62
+ }
63
+
64
+ /// Collection of fonts.
65
+ #[derive(Clone)]
66
+ pub struct Collection {
67
+ inner: Inner,
68
+ query_state: query::QueryState,
69
+ }
70
+
71
+ impl Collection {
72
+ /// Creates a new collection with the given options.
73
+ ///
74
+ /// If `fontique` was compiled with the `"system"` feature and
75
+ /// [`CollectionOptions::system_fonts`] was set to `true` when
76
+ /// creating this collection, then it will register the fonts
77
+ /// available on the system.
78
+ ///
79
+ /// Additional fonts can be registered via [`Collection::register_fonts`]
80
+ /// and providing it with the data for those fonts.
81
+ pub fn new(options: CollectionOptions) -> Self {
82
+ Self {
83
+ inner: Inner::new(options),
84
+ query_state: query::QueryState::default(),
85
+ }
86
+ }
87
+
88
+ /// Converts an unshared collection into a shared collection
89
+ #[cfg(feature = "std")]
90
+ pub fn make_shared(&mut self) {
91
+ self.inner.make_shared();
92
+ }
93
+
94
+ /// Load system fonts. If system fonts are already loaded then this does nothing.
95
+ pub fn load_system_fonts(&mut self) {
96
+ if self.inner.system.is_none() {
97
+ self.inner.load_system_fonts();
98
+ }
99
+ }
100
+
101
+ /// Loads all fonts contained within the specified directory(s)
102
+ #[cfg(feature = "std")]
103
+ pub fn load_fonts_from_paths(&mut self, paths: impl IntoIterator<Item = impl AsRef<Path>>) {
104
+ self.inner.load_fonts_from_paths(paths);
105
+ }
106
+
107
+ /// Returns an iterator over all available family names in the collection.
108
+ ///
109
+ /// If `fontique` was compiled with the `"system"` feature, then it will
110
+ /// include system fonts after the registered fonts.
111
+ pub fn family_names(&mut self) -> impl Iterator<Item = &str> + '_ + Clone {
112
+ self.inner.family_names()
113
+ }
114
+
115
+ /// Returns an iterator over all available family identifiers in the collection.
116
+ ///
117
+ /// If `fontique` was compiled with the `"system"` feature, then it will
118
+ /// include system fonts after the registered fonts.
119
+ pub fn family_ids(&mut self) -> impl Iterator<Item = FamilyId> + '_ + Clone {
120
+ self.inner.family_ids()
121
+ }
122
+
123
+ /// Returns the family identifier for the given family name.
124
+ pub fn family_id(&mut self, name: &str) -> Option<FamilyId> {
125
+ self.inner.family_id(name)
126
+ }
127
+
128
+ /// Returns the family name for the given family identifier.
129
+ pub fn family_name(&mut self, id: FamilyId) -> Option<&str> {
130
+ self.inner.family_name(id)
131
+ }
132
+
133
+ /// Returns the family object for the given family identifier.
134
+ pub fn family(&mut self, id: FamilyId) -> Option<FamilyInfo> {
135
+ self.inner.family(id)
136
+ }
137
+
138
+ /// Returns the family object for the given name.
139
+ pub fn family_by_name(&mut self, name: &str) -> Option<FamilyInfo> {
140
+ self.inner.family_by_name(name)
141
+ }
142
+
143
+ /// Returns an iterator over the family identifiers for the given
144
+ /// generic family.
145
+ pub fn generic_families(
146
+ &mut self,
147
+ family: GenericFamily,
148
+ ) -> impl Iterator<Item = FamilyId> + '_ + Clone {
149
+ self.inner.generic_families(family)
150
+ }
151
+
152
+ /// Replaces the set of family identifiers associated with the given generic
153
+ /// family.
154
+ pub fn set_generic_families(
155
+ &mut self,
156
+ generic: GenericFamily,
157
+ families: impl Iterator<Item = FamilyId>,
158
+ ) {
159
+ self.inner.set_generic_families(generic, families);
160
+ }
161
+
162
+ /// Appends the set of family identifiers to the given generic family.
163
+ pub fn append_generic_families(
164
+ &mut self,
165
+ generic: GenericFamily,
166
+ families: impl Iterator<Item = FamilyId>,
167
+ ) {
168
+ self.inner.append_generic_families(generic, families);
169
+ }
170
+
171
+ /// Returns an iterator over the fallback families for the given
172
+ /// key.
173
+ pub fn fallback_families(
174
+ &mut self,
175
+ key: impl Into<FallbackKey>,
176
+ ) -> impl Iterator<Item = FamilyId> + '_ + Clone {
177
+ self.inner.fallback_families(key)
178
+ }
179
+
180
+ /// Replaces the set of family identifiers associated with the fallback
181
+ /// key.
182
+ pub fn set_fallbacks(
183
+ &mut self,
184
+ key: impl Into<FallbackKey>,
185
+ families: impl Iterator<Item = FamilyId>,
186
+ ) -> bool {
187
+ self.inner.set_fallbacks(key, families)
188
+ }
189
+
190
+ /// Appends the set of family identifiers to the given fallback key.
191
+ pub fn append_fallbacks(
192
+ &mut self,
193
+ key: impl Into<FallbackKey>,
194
+ families: impl Iterator<Item = FamilyId>,
195
+ ) -> bool {
196
+ self.inner.append_fallbacks(key, families)
197
+ }
198
+
199
+ /// Enables fallback to registered fonts after the requested and script families.
200
+ ///
201
+ /// Registration order is preserved. The supplied last-resort families are
202
+ /// tried after all other registered fonts, including fonts registered later.
203
+ /// System fonts are not enumerated by this policy.
204
+ pub fn set_registered_fallbacks(&mut self, last_resort: impl Iterator<Item = FamilyId>) {
205
+ self.inner.set_registered_fallbacks(last_resort);
206
+ }
207
+
208
+ /// Returns an object for selecting fonts from this collection.
209
+ pub fn query<'a>(&'a mut self, source_cache: &'a mut SourceCache) -> Query<'a> {
210
+ Query::new(self, source_cache)
211
+ }
212
+
213
+ /// Registers all fonts that exist in the given data.
214
+ ///
215
+ /// Returns a list of pairs each containing the family identifier and fonts
216
+ /// added to that family.
217
+ ///
218
+ /// Optionally, you can override various aspects of the font's metadata.
219
+ pub fn register_fonts(
220
+ &mut self,
221
+ data: Blob<u8>,
222
+ info_override: Option<FontInfoOverride<'_>>,
223
+ ) -> Vec<(FamilyId, Vec<FontInfo>)> {
224
+ self.inner.register_fonts(data, info_override)
225
+ }
226
+
227
+ /// Unregisters the font with the given attributes from the given family.
228
+ ///
229
+ /// Returns true if a font was removed from the family, false otherwise.
230
+ pub fn unregister_font(
231
+ &mut self,
232
+ family: FamilyId,
233
+ width: FontWidth,
234
+ style: FontStyle,
235
+ weight: FontWeight,
236
+ ) -> bool {
237
+ self.inner.unregister_font(family, width, style, weight)
238
+ }
239
+
240
+ /// Clears this collection. Un-registers all fonts previously registered via
241
+ /// [`Self::register_fonts`], and unsets all previously-set generic families
242
+ /// and fallbacks. This will not remove any system fonts.
243
+ pub fn clear(&mut self) {
244
+ self.inner.clear();
245
+ }
246
+ }
247
+
248
+ impl Default for Collection {
249
+ fn default() -> Self {
250
+ Self::new(CollectionOptions::default())
251
+ }
252
+ }
253
+
254
+ /// Collection of fonts.
255
+ #[derive(Clone)]
256
+ struct Inner {
257
+ system: Option<System>,
258
+ data: CommonData,
259
+ #[allow(unused)]
260
+ shared: Option<Arc<Shared>>,
261
+ #[allow(unused)]
262
+ shared_version: CounterInt,
263
+ fallback_cache: FallbackCache,
264
+ }
265
+
266
+ impl Inner {
267
+ /// Creates a new collection with the given options.
268
+ pub fn new(options: CollectionOptions) -> Self {
269
+ let system = options.system_fonts.then(System::new);
270
+ let shared = options.shared.then(|| Arc::new(Shared::default()));
271
+ Self {
272
+ system,
273
+ data: CommonData::default(),
274
+ shared,
275
+ shared_version: 0,
276
+ fallback_cache: FallbackCache::default(),
277
+ }
278
+ }
279
+
280
+ #[cfg(feature = "std")]
281
+ pub fn make_shared(&mut self) {
282
+ if self.shared.is_none() {
283
+ self.shared = Some(Arc::new(Shared {
284
+ data: Mutex::new(self.data.clone()),
285
+ version: AtomicCounter::new(self.shared_version),
286
+ }));
287
+ }
288
+ }
289
+
290
+ /// Load system fonts. If system fonts are already loaded then they will be reloaded.
291
+ pub fn load_system_fonts(&mut self) {
292
+ self.system = Some(System::new());
293
+ }
294
+
295
+ /// Returns an iterator over all available family names in the collection.
296
+ ///
297
+ /// This includes both system and registered fonts.
298
+ pub fn family_names(&mut self) -> impl Iterator<Item = &str> + '_ + Clone {
299
+ self.sync_shared();
300
+ FamilyNames {
301
+ ours: self.data.family_names.iter(),
302
+ system: self.system.as_ref().map(|sys| sys.family_names.iter()),
303
+ }
304
+ .map(|name| name.name())
305
+ }
306
+
307
+ /// Returns an iterator over all available family identifiers in the collection.
308
+ ///
309
+ /// This includes both system and registered fonts.
310
+ pub fn family_ids(&mut self) -> impl Iterator<Item = FamilyId> + '_ + Clone {
311
+ self.sync_shared();
312
+ self.data.family_names.ids().chain(
313
+ self.system
314
+ .as_ref()
315
+ .map(|sys| sys.family_names.ids())
316
+ .into_iter()
317
+ .flatten(),
318
+ )
319
+ }
320
+
321
+ /// Returns the family identifier for the given family name.
322
+ pub fn family_id(&mut self, name: &str) -> Option<FamilyId> {
323
+ self.sync_shared();
324
+ self.data
325
+ .family_names
326
+ .get(name)
327
+ .or_else(|| {
328
+ self.system
329
+ .as_ref()
330
+ .and_then(|sys| sys.family_names.get(name))
331
+ })
332
+ .map(|n| n.id())
333
+ }
334
+
335
+ /// Returns the family name for the given family identifier.
336
+ pub fn family_name(&mut self, id: FamilyId) -> Option<&str> {
337
+ self.sync_shared();
338
+ self.data
339
+ .family_names
340
+ .get_by_id(id)
341
+ .or_else(|| {
342
+ self.system
343
+ .as_ref()
344
+ .and_then(|sys| sys.family_names.get_by_id(id))
345
+ })
346
+ .map(|name| name.name())
347
+ }
348
+
349
+ /// Returns the family object for the given family identifier.
350
+ pub fn family(&mut self, id: FamilyId) -> Option<FamilyInfo> {
351
+ self.sync_shared();
352
+ if let Some(family) = self.data.families.get(&id) {
353
+ family.as_ref().cloned()
354
+ } else {
355
+ #[cfg(feature = "system")]
356
+ if let Some(system) = &self.system {
357
+ let family = system.fonts.lock().unwrap().family(id);
358
+ self.data.families.insert(id, family.clone());
359
+ family
360
+ } else {
361
+ None
362
+ }
363
+ #[cfg(not(feature = "system"))]
364
+ {
365
+ None
366
+ }
367
+ }
368
+ }
369
+
370
+ /// Returns the family object for the given name.
371
+ pub fn family_by_name(&mut self, name: &str) -> Option<FamilyInfo> {
372
+ if let Some(id) = self.family_id(name) {
373
+ self.family(id)
374
+ } else {
375
+ None
376
+ }
377
+ }
378
+
379
+ /// Returns an iterator over the family identifiers for the given
380
+ /// generic family.
381
+ pub fn generic_families(
382
+ &mut self,
383
+ family: GenericFamily,
384
+ ) -> impl Iterator<Item = FamilyId> + '_ + Clone {
385
+ self.sync_shared();
386
+ GenericFamilies {
387
+ ours: self.data.generic_families.get(family).iter().copied(),
388
+ system: self
389
+ .system
390
+ .as_ref()
391
+ .map(|sys| sys.generic_families.get(family).iter().copied()),
392
+ }
393
+ }
394
+
395
+ /// Replaces the set of family identifiers associated with the given generic
396
+ /// family.
397
+ pub fn set_generic_families(
398
+ &mut self,
399
+ generic: GenericFamily,
400
+ families: impl Iterator<Item = FamilyId>,
401
+ ) {
402
+ self.sync_shared();
403
+ #[cfg(feature = "std")]
404
+ if let Some(shared) = &self.shared {
405
+ shared
406
+ .data
407
+ .lock()
408
+ .unwrap()
409
+ .generic_families
410
+ .set(generic, families);
411
+ shared.bump_version();
412
+ } else {
413
+ self.data.generic_families.set(generic, families);
414
+ }
415
+ #[cfg(not(feature = "std"))]
416
+ self.data.generic_families.set(generic, families);
417
+ }
418
+
419
+ /// Appends the set of family identifiers to the given generic family.
420
+ pub fn append_generic_families(
421
+ &mut self,
422
+ generic: GenericFamily,
423
+ families: impl Iterator<Item = FamilyId>,
424
+ ) {
425
+ self.sync_shared();
426
+ #[cfg(feature = "std")]
427
+ if let Some(shared) = &self.shared {
428
+ shared
429
+ .data
430
+ .lock()
431
+ .unwrap()
432
+ .generic_families
433
+ .append(generic, families);
434
+ shared.bump_version();
435
+ } else {
436
+ self.data.generic_families.append(generic, families);
437
+ }
438
+ #[cfg(not(feature = "std"))]
439
+ self.data.generic_families.append(generic, families);
440
+ }
441
+
442
+ /// Returns an iterator over the fallback families for the given
443
+ /// key.
444
+ pub fn fallback_families(
445
+ &mut self,
446
+ key: impl Into<FallbackKey>,
447
+ ) -> impl Iterator<Item = FamilyId> + '_ + Clone {
448
+ let selector = key.into();
449
+ let script = selector.script();
450
+ let lang_key = selector.locale();
451
+ if self.fallback_cache.script != Some(script) || self.fallback_cache.language != lang_key {
452
+ self.sync_shared();
453
+ self.fallback_cache.reset();
454
+ #[cfg(feature = "system")]
455
+ if let Some(families) = self.data.fallbacks.get(selector) {
456
+ self.fallback_cache.set(script, lang_key, families);
457
+ } else if let Some(system) = self.system.as_ref() {
458
+ // Some platforms don't need mut System
459
+ #[allow(unused_mut)]
460
+ let mut system = system.fonts.lock().unwrap();
461
+ if let Some(family) = system.fallback(selector) {
462
+ self.data.fallbacks.set(selector, core::iter::once(family));
463
+ self.fallback_cache.set(script, lang_key, &[family]);
464
+ }
465
+ }
466
+ #[cfg(not(feature = "system"))]
467
+ if let Some(families) = self.data.fallbacks.get(selector) {
468
+ self.fallback_cache.set(script, lang_key, families);
469
+ }
470
+ }
471
+ self.fallback_cache.families.iter().copied()
472
+ }
473
+
474
+ /// Replaces the set of family identifiers associated with the fallback
475
+ /// key.
476
+ pub fn set_fallbacks(
477
+ &mut self,
478
+ key: impl Into<FallbackKey>,
479
+ families: impl Iterator<Item = FamilyId>,
480
+ ) -> bool {
481
+ self.sync_shared();
482
+ self.fallback_cache.reset();
483
+ #[cfg(feature = "std")]
484
+ if let Some(shared) = &self.shared {
485
+ let result = shared.data.lock().unwrap().fallbacks.set(key, families);
486
+ shared.bump_version();
487
+ result
488
+ } else {
489
+ self.data.fallbacks.set(key, families)
490
+ }
491
+ #[cfg(not(feature = "std"))]
492
+ self.data.fallbacks.set(key, families)
493
+ }
494
+
495
+ /// Appends the set of family identifiers to the given fallback key.
496
+ pub fn append_fallbacks(
497
+ &mut self,
498
+ key: impl Into<FallbackKey>,
499
+ families: impl Iterator<Item = FamilyId>,
500
+ ) -> bool {
501
+ self.sync_shared();
502
+ self.fallback_cache.reset();
503
+ #[cfg(feature = "std")]
504
+ if let Some(shared) = &self.shared {
505
+ let result = shared.data.lock().unwrap().fallbacks.append(key, families);
506
+ shared.bump_version();
507
+ result
508
+ } else {
509
+ self.data.fallbacks.append(key, families)
510
+ }
511
+ #[cfg(not(feature = "std"))]
512
+ self.data.fallbacks.append(key, families)
513
+ }
514
+
515
+ /// Loads all fonts that exist in the specified directory(s)
516
+ #[cfg(feature = "std")]
517
+ pub fn load_fonts_from_paths(&mut self, paths: impl IntoIterator<Item = impl AsRef<Path>>) {
518
+ #[cfg(feature = "std")]
519
+ if let Some(shared) = &self.shared {
520
+ shared.data.lock().unwrap().load_fonts_from_paths(paths);
521
+ shared.bump_version();
522
+ } else {
523
+ self.data.load_fonts_from_paths(paths);
524
+ }
525
+ #[cfg(not(feature = "std"))]
526
+ self.data.register_fonts(paths)
527
+ }
528
+
529
+ /// Registers all fonts that exist in the given data.
530
+ ///
531
+ /// Returns a list of pairs each containing the family identifier and fonts
532
+ /// added to that family.
533
+ pub fn register_fonts(
534
+ &mut self,
535
+ data: Blob<u8>,
536
+ info_override: Option<FontInfoOverride<'_>>,
537
+ ) -> Vec<(FamilyId, Vec<FontInfo>)> {
538
+ #[cfg(feature = "std")]
539
+ if let Some(shared) = &self.shared {
540
+ let result = shared
541
+ .data
542
+ .lock()
543
+ .unwrap()
544
+ .register_fonts(data, info_override);
545
+ shared.bump_version();
546
+ result
547
+ } else {
548
+ self.data.register_fonts(data, info_override)
549
+ }
550
+ #[cfg(not(feature = "std"))]
551
+ self.data.register_fonts(data, info_override)
552
+ }
553
+
554
+ /// Unregisters the font with the given attributes from the given family.
555
+ ///
556
+ /// Returns true if a font was removed from the family, false otherwise.
557
+ pub fn unregister_font(
558
+ &mut self,
559
+ family: FamilyId,
560
+ width: FontWidth,
561
+ style: FontStyle,
562
+ weight: FontWeight,
563
+ ) -> bool {
564
+ #[cfg(feature = "std")]
565
+ if let Some(shared) = &self.shared {
566
+ let result = shared
567
+ .data
568
+ .lock()
569
+ .unwrap()
570
+ .unregister_font(family, width, style, weight);
571
+ shared.bump_version();
572
+ result.is_some()
573
+ } else {
574
+ self.data
575
+ .unregister_font(family, width, style, weight)
576
+ .is_some()
577
+ }
578
+ #[cfg(not(feature = "std"))]
579
+ self.data
580
+ .unregister_font(family, width, style, weight)
581
+ .is_some()
582
+ }
583
+
584
+ /// Clears this collection. Un-registers all fonts previously registered via
585
+ /// [`Self::register_fonts`], and unsets all previously-set generic families
586
+ /// and fallbacks. This will not remove any system fonts.
587
+ pub fn clear(&mut self) {
588
+ #[cfg(feature = "std")]
589
+ if let Some(shared) = &self.shared {
590
+ shared.data.lock().unwrap().clear();
591
+ shared.bump_version();
592
+ } else {
593
+ self.data.clear();
594
+ }
595
+ self.data.clear();
596
+ }
597
+
598
+ fn set_registered_fallbacks(&mut self, last_resort: impl Iterator<Item = FamilyId>) {
599
+ self.sync_shared();
600
+ let families = Some(last_resort.collect());
601
+ #[cfg(feature = "std")]
602
+ if let Some(shared) = &self.shared {
603
+ shared.data.lock().unwrap().registered_fallbacks = families;
604
+ shared.bump_version();
605
+ } else {
606
+ self.data.registered_fallbacks = families;
607
+ }
608
+ #[cfg(not(feature = "std"))]
609
+ {
610
+ self.data.registered_fallbacks = families;
611
+ }
612
+ }
613
+
614
+ fn registered_fallbacks(&mut self) -> Vec<FamilyId> {
615
+ self.sync_shared();
616
+ let Some(last_resort) = &self.data.registered_fallbacks else {
617
+ return Vec::new();
618
+ };
619
+ let mut families: Vec<_> = self
620
+ .data
621
+ .family_names
622
+ .ids()
623
+ .filter(|id| !last_resort.contains(id))
624
+ .collect();
625
+ // Family IDs are allocated in registration order; the name map is hashed.
626
+ families.sort_unstable();
627
+ for id in last_resort {
628
+ if !families.contains(id) {
629
+ families.push(*id);
630
+ }
631
+ }
632
+ families
633
+ }
634
+
635
+ fn sync_shared(&mut self) {
636
+ #[cfg(feature = "std")]
637
+ if let Some(shared) = &self.shared {
638
+ let version = shared.version.load(Ordering::Acquire);
639
+ if self.shared_version != version {
640
+ // This is an ugly deep copy, but the assumption is that
641
+ // modifications to font collections are fairly rare.
642
+ // If this becomes a problem, do more fine grained tracking
643
+ // of changes.
644
+ self.data = shared.data.lock().unwrap().clone();
645
+ self.shared_version = version;
646
+ self.fallback_cache.reset();
647
+ }
648
+ }
649
+ }
650
+ }
651
+
652
+ #[derive(Clone)]
653
+ struct FamilyNames<I> {
654
+ ours: I,
655
+ system: Option<I>,
656
+ }
657
+
658
+ impl<'a, I> Iterator for FamilyNames<I>
659
+ where
660
+ I: Iterator<Item = &'a FamilyName> + 'a,
661
+ {
662
+ type Item = &'a FamilyName;
663
+
664
+ fn next(&mut self) -> Option<Self::Item> {
665
+ if let Some(ours) = self.ours.next() {
666
+ return Some(ours);
667
+ }
668
+ self.system.as_mut()?.next()
669
+ }
670
+ }
671
+
672
+ #[derive(Clone)]
673
+ struct GenericFamilies<I> {
674
+ ours: I,
675
+ system: Option<I>,
676
+ }
677
+
678
+ impl<I> Iterator for GenericFamilies<I>
679
+ where
680
+ I: Iterator<Item = FamilyId>,
681
+ {
682
+ type Item = FamilyId;
683
+
684
+ fn next(&mut self) -> Option<Self::Item> {
685
+ if let Some(ours) = self.ours.next() {
686
+ return Some(ours);
687
+ }
688
+ self.system.as_mut()?.next()
689
+ }
690
+ }
691
+
692
+ #[derive(Clone, Default)]
693
+ struct FallbackCache {
694
+ script: Option<Script>,
695
+ language: Option<Language>,
696
+ families: Vec<FamilyId>,
697
+ }
698
+
699
+ impl FallbackCache {
700
+ fn reset(&mut self) {
701
+ self.script = None;
702
+ self.language = None;
703
+ self.families.clear();
704
+ }
705
+
706
+ fn set(&mut self, script: Script, language: Option<Language>, families: &[FamilyId]) {
707
+ self.script = Some(script);
708
+ self.language = language;
709
+ self.families.clear();
710
+ self.families.extend_from_slice(families);
711
+ }
712
+ }
713
+
714
+ /// Data taken from the system font collection.
715
+ #[derive(Clone)]
716
+ struct System {
717
+ #[cfg(feature = "system")]
718
+ fonts: Arc<Mutex<SystemFonts>>,
719
+ family_names: Arc<FamilyNameMap>,
720
+ generic_families: Arc<GenericFamilyMap>,
721
+ }
722
+
723
+ impl System {
724
+ fn new() -> Self {
725
+ let fonts = SystemFonts::new();
726
+ let family_names = fonts.name_map.clone();
727
+ let generic_families = fonts.generic_families.clone();
728
+ #[cfg(feature = "system")]
729
+ let fonts = Arc::new(Mutex::new(fonts));
730
+ Self {
731
+ #[cfg(feature = "system")]
732
+ fonts,
733
+ family_names,
734
+ generic_families,
735
+ }
736
+ }
737
+ }
738
+
739
+ /// Common data for base and shared collections.
740
+ #[derive(Clone, Default)]
741
+ struct CommonData {
742
+ family_names: FamilyNameMap,
743
+ families: FamilyMap,
744
+ generic_families: GenericFamilyMap,
745
+ fallbacks: FallbackMap,
746
+ registered_fallbacks: Option<Vec<FamilyId>>,
747
+ }
748
+
749
+ impl CommonData {
750
+ #[cfg(feature = "std")]
751
+ fn load_fonts_from_paths(&mut self, paths: impl IntoIterator<Item = impl AsRef<Path>>) {
752
+ let mut families: HashMap<FamilyId, (FamilyName, Vec<FontInfo>)> = HashMap::default();
753
+ let mut scratch_family_name = String::default();
754
+ crate::scan::scan_paths(paths, 16, |scanned_font| {
755
+ let source = SourceInfo {
756
+ id: SourceId::new(),
757
+ kind: SourceKind::Path(Arc::from(scanned_font.path.unwrap())),
758
+ };
759
+
760
+ // `scan_paths` invokes this callback once per face (each face of a
761
+ // collection is reported separately), so register exactly this font.
762
+ // Re-scanning the file here would register duplicate faces.
763
+ families.clear();
764
+ self.register_scanned_font(
765
+ scanned_font,
766
+ &source,
767
+ None,
768
+ &mut scratch_family_name,
769
+ &mut families,
770
+ );
771
+ self.merge_families(&families);
772
+ });
773
+ }
774
+
775
+ fn register_fonts(
776
+ &mut self,
777
+ data: Blob<u8>,
778
+ info_override: Option<FontInfoOverride<'_>>,
779
+ ) -> Vec<(FamilyId, Vec<FontInfo>)> {
780
+ let mut families: HashMap<FamilyId, (FamilyName, Vec<FontInfo>)> = HashMap::default();
781
+ let mut scratch_family_name = String::default();
782
+
783
+ let source = SourceInfo {
784
+ id: SourceId::new(),
785
+ kind: SourceKind::Memory(data.clone()),
786
+ };
787
+
788
+ super::scan::scan_memory(data.as_ref(), |scanned_font| {
789
+ self.register_scanned_font(
790
+ scanned_font,
791
+ &source,
792
+ info_override,
793
+ &mut scratch_family_name,
794
+ &mut families,
795
+ );
796
+ });
797
+
798
+ self.merge_families(&families);
799
+
800
+ families
801
+ .into_iter()
802
+ .map(|(id, (_, fonts))| (id, fonts))
803
+ .collect()
804
+ }
805
+
806
+ fn register_scanned_font(
807
+ &mut self,
808
+ scanned_font: &crate::scan::ScannedFont<'_>,
809
+ source: &SourceInfo,
810
+ info_override: Option<FontInfoOverride<'_>>,
811
+ scratch_family_name: &mut String,
812
+ families: &mut HashMap<FamilyId, (FamilyName, Vec<FontInfo>)>,
813
+ ) {
814
+ scratch_family_name.clear();
815
+
816
+ let family_name =
817
+ if let Some(override_family_name) = info_override.and_then(|o| o.family_name) {
818
+ override_family_name
819
+ } else {
820
+ let family_chars = scanned_font
821
+ .english_or_first_name(NameId::TYPOGRAPHIC_FAMILY_NAME)
822
+ .or_else(|| scanned_font.english_or_first_name(NameId::FAMILY_NAME))
823
+ .map(|name| name.chars());
824
+ let Some(family_chars) = family_chars else {
825
+ return;
826
+ };
827
+ scratch_family_name.extend(family_chars);
828
+
829
+ #[allow(clippy::needless_borrow)] // false positive
830
+ &scratch_family_name
831
+ };
832
+
833
+ if family_name.is_empty() {
834
+ return;
835
+ }
836
+
837
+ let Some(mut font) =
838
+ FontInfo::from_font_ref(&scanned_font.font, source.clone(), scanned_font.index)
839
+ else {
840
+ return;
841
+ };
842
+
843
+ if let Some(info_override) = info_override.as_ref() {
844
+ font.apply_override(info_override);
845
+ }
846
+
847
+ let name = self.family_names.get_or_insert(family_name);
848
+ families
849
+ .entry(name.id())
850
+ .or_insert_with(|| (name, Vec::default()))
851
+ .1
852
+ .push(font);
853
+ }
854
+
855
+ fn merge_families(&mut self, families: &HashMap<FamilyId, (FamilyName, Vec<FontInfo>)>) {
856
+ for (id, (name, fonts)) in families.iter() {
857
+ if let Some(Some(family)) = self.families.get_mut(id) {
858
+ let new_fonts = family.fonts().iter().chain(fonts).cloned();
859
+ *family = FamilyInfo::new(name.clone(), new_fonts);
860
+ } else {
861
+ let family = FamilyInfo::new(name.clone(), fonts.iter().cloned());
862
+ self.families.insert(*id, Some(family));
863
+ }
864
+ }
865
+ }
866
+
867
+ fn unregister_font(
868
+ &mut self,
869
+ family: FamilyId,
870
+ width: FontWidth,
871
+ style: FontStyle,
872
+ weight: FontWeight,
873
+ ) -> Option<()> {
874
+ let family_name = self.family_names.get_by_id(family)?;
875
+ let family = self.families.get_mut(&family)?.as_mut()?;
876
+
877
+ let new_fonts = family
878
+ .fonts()
879
+ .iter()
880
+ .filter(|f| f.width() != width || f.style() != style || f.weight() != weight)
881
+ .cloned();
882
+ *family = FamilyInfo::new(family_name.clone(), new_fonts);
883
+
884
+ Some(())
885
+ }
886
+
887
+ fn clear(&mut self) {
888
+ *self = Self::default();
889
+ }
890
+ }
891
+
892
+ /// Synchronized shared collection data.
893
+ #[derive(Default)]
894
+ struct Shared {
895
+ #[allow(unused)]
896
+ version: AtomicCounter,
897
+ #[cfg(feature = "std")]
898
+ #[allow(unused)]
899
+ data: Mutex<CommonData>,
900
+ }
901
+
902
+ impl Shared {
903
+ #[cfg(feature = "std")]
904
+ fn bump_version(&self) {
905
+ self.version.fetch_add(1, Ordering::Release);
906
+ }
907
+ }
908
+
909
+ #[test]
910
+ #[cfg(all(
911
+ feature = "std",
912
+ any(
913
+ target_os = "linux",
914
+ target_os = "freebsd",
915
+ target_os = "macos",
916
+ target_os = "windows"
917
+ )
918
+ ))]
919
+ fn make_shared_matches_local() {
920
+ use crate::{Collection, CollectionOptions};
921
+
922
+ let mut collection = Collection::new(CollectionOptions {
923
+ shared: false,
924
+ system_fonts: false,
925
+ });
926
+
927
+ let font_dirs: Vec<std::path::PathBuf> = [
928
+ #[cfg(target_os = "macos")]
929
+ "/Library/Fonts",
930
+ #[cfg(target_os = "linux")]
931
+ "/usr/share/fonts",
932
+ #[cfg(target_os = "freebsd")]
933
+ "/usr/local/share/fonts",
934
+ #[cfg(target_os = "windows")]
935
+ "C:\\Windows\\Fonts",
936
+ ]
937
+ .iter()
938
+ .map(std::path::PathBuf::from)
939
+ .filter(|p| p.is_dir())
940
+ .collect();
941
+
942
+ if font_dirs.is_empty() {
943
+ return;
944
+ }
945
+
946
+ collection.load_fonts_from_paths(&font_dirs);
947
+
948
+ let names_before: Vec<String> = collection.family_names().map(String::from).collect();
949
+
950
+ collection.make_shared();
951
+
952
+ let names_after: Vec<String> = collection.family_names().map(String::from).collect();
953
+
954
+ assert_eq!(names_before.len(), names_after.len());
955
+ }
956
+
957
+ // Regression test for https://github.com/linebender/parley/issues/683: loading
958
+ // fonts from a directory used to register the same face several times because a
959
+ // shared accumulator was never cleared and every callback re-scanned the whole
960
+ // file. Each face should appear exactly once, identified by its source and index.
961
+ #[test]
962
+ #[cfg(all(
963
+ feature = "std",
964
+ any(
965
+ target_os = "linux",
966
+ target_os = "freebsd",
967
+ target_os = "macos",
968
+ target_os = "windows"
969
+ )
970
+ ))]
971
+ fn load_fonts_from_paths_registers_each_face_once() {
972
+ use crate::{Collection, CollectionOptions};
973
+ use std::collections::HashSet;
974
+
975
+ let mut collection = Collection::new(CollectionOptions {
976
+ shared: false,
977
+ system_fonts: false,
978
+ });
979
+
980
+ let font_dirs: Vec<std::path::PathBuf> = [
981
+ #[cfg(target_os = "macos")]
982
+ "/Library/Fonts",
983
+ #[cfg(target_os = "linux")]
984
+ "/usr/share/fonts",
985
+ #[cfg(target_os = "freebsd")]
986
+ "/usr/local/share/fonts",
987
+ #[cfg(target_os = "windows")]
988
+ "C:\\Windows\\Fonts",
989
+ ]
990
+ .iter()
991
+ .map(std::path::PathBuf::from)
992
+ .filter(|p| p.is_dir())
993
+ .collect();
994
+
995
+ if font_dirs.is_empty() {
996
+ return;
997
+ }
998
+
999
+ collection.load_fonts_from_paths(&font_dirs);
1000
+
1001
+ let family_ids: Vec<_> = collection.family_ids().collect();
1002
+ for id in family_ids {
1003
+ let Some(family) = collection.family(id) else {
1004
+ continue;
1005
+ };
1006
+ let mut seen = HashSet::new();
1007
+ for font in family.fonts() {
1008
+ let face = (font.source().id(), font.index());
1009
+ assert!(
1010
+ seen.insert(face),
1011
+ "family {:?} contains a duplicate face {face:?}",
1012
+ collection.family_name(id),
1013
+ );
1014
+ }
1015
+ }
1016
+ }