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,483 @@
1
+ // Copyright 2024 the Parley Authors
2
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
3
+
4
+ //! Implementation of the CSS font matching algorithm.
5
+
6
+ use super::font::FontInfo;
7
+ use crate::{FontStyle, FontWeight, FontWidth};
8
+ use smallvec::SmallVec;
9
+
10
+ const DEFAULT_OBLIQUE_ANGLE: f32 = 14.0;
11
+
12
+ pub(crate) fn match_fonts(
13
+ set: &[FontInfo],
14
+ width: FontWidth,
15
+ style: FontStyle,
16
+ weight: FontWeight,
17
+ synthesize_style: bool,
18
+ ) -> impl Iterator<Item = usize> + '_ {
19
+ let best = match_font(set, width, style, weight, synthesize_style).map(|index| {
20
+ let font = &set[index];
21
+ (font.width(), font.style(), font.weight())
22
+ });
23
+ set.iter().enumerate().filter_map(move |(index, font)| {
24
+ let (best_width, best_style, best_weight) = best?;
25
+ // We need to return each font with the best attributes, as there might be multiple subsets.
26
+ (font.width() == best_width && font.style() == best_style && font.weight() == best_weight)
27
+ .then_some(index)
28
+ })
29
+ }
30
+
31
+ pub fn match_font(
32
+ set: &[FontInfo],
33
+ width: FontWidth,
34
+ style: FontStyle,
35
+ weight: FontWeight,
36
+ synthesize_style: bool,
37
+ ) -> Option<usize> {
38
+ const OBLIQUE_THRESHOLD: f32 = DEFAULT_OBLIQUE_ANGLE;
39
+ match set.len() {
40
+ 0 => return None,
41
+ 1 => return Some(0),
42
+ _ => {}
43
+ }
44
+ #[derive(Copy, Clone)]
45
+ struct Candidate {
46
+ index: usize,
47
+ width: i32,
48
+ style: FontStyle,
49
+ weight: f32,
50
+ has_slnt: bool,
51
+ }
52
+ let mut set: SmallVec<[Candidate; 16]> = set
53
+ .iter()
54
+ .enumerate()
55
+ .map(|(i, font)| Candidate {
56
+ index: i,
57
+ width: (font.width().ratio() * 100.0) as i32,
58
+ style: font.style(),
59
+ weight: font.weight().value(),
60
+ has_slnt: font.has_slant_axis(),
61
+ })
62
+ .collect();
63
+ let width = (width.ratio() * 100.0) as i32;
64
+ let weight = weight.value();
65
+ // font-width is tried first:
66
+ let mut use_width = set[0].width;
67
+ if !set.iter().any(|f| f.width == width) {
68
+ // If the desired width value is less than or equal to 100%...
69
+ if width <= 100 {
70
+ // width values below the desired width value are checked in
71
+ // descending order...
72
+ if let Some(found) = set
73
+ .iter()
74
+ .filter(|f| f.width < width)
75
+ .max_by_key(|f| f.width)
76
+ {
77
+ use_width = found.width;
78
+ }
79
+ // followed by width values above the desired width value in
80
+ // ascending order until a match is found.
81
+ else if let Some(found) = set
82
+ .iter()
83
+ .filter(|f| f.width > width)
84
+ .min_by_key(|f| f.width)
85
+ {
86
+ use_width = found.width;
87
+ }
88
+ }
89
+ // Otherwise, ...
90
+ else {
91
+ // width values above the desired width value are checked in
92
+ // ascending order...
93
+ if let Some(found) = set
94
+ .iter()
95
+ .filter(|f| f.width > width)
96
+ .min_by_key(|f| f.width)
97
+ {
98
+ use_width = found.width;
99
+ }
100
+ // followed by width values below the desired width value in
101
+ // descending order until a match is found.
102
+ else if let Some(found) = set
103
+ .iter()
104
+ .filter(|f| f.width < width)
105
+ .max_by_key(|f| f.width)
106
+ {
107
+ use_width = found.width;
108
+ }
109
+ }
110
+ } else {
111
+ use_width = width;
112
+ }
113
+ set.retain(|f| f.width == use_width);
114
+ use core::cmp::Ordering::*;
115
+ let oblique_fonts = set.iter().filter_map(|f| oblique_style(f.style));
116
+ // font-style is tried next:
117
+ // NOTE: this code uses an oblique threshold of 14deg rather than
118
+ // the current value of 20deg in the spec.
119
+ // See: https://github.com/w3c/csswg-drafts/issues/2295
120
+ let mut use_style = style;
121
+ let mut _use_slnt = false;
122
+ if !set.iter().any(|f| f.style == use_style) {
123
+ // If the value of font-style is italic:
124
+ if style == FontStyle::Italic {
125
+ // oblique values greater than or equal to 14deg are checked in
126
+ // ascending order
127
+ if let Some(found) = oblique_fonts
128
+ .clone()
129
+ .filter(|(_, a)| *a >= OBLIQUE_THRESHOLD)
130
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
131
+ {
132
+ use_style = found.0;
133
+ }
134
+ // followed by positive oblique values below 14deg in descending order
135
+ else if let Some(found) = oblique_fonts
136
+ .clone()
137
+ .filter(|(_, a)| *a > 0. && *a < OBLIQUE_THRESHOLD)
138
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
139
+ {
140
+ use_style = found.0;
141
+ }
142
+ // If no match is found, oblique values less than or equal to 0deg
143
+ // are checked in descending order until a match is found.
144
+ else if let Some(found) = oblique_fonts
145
+ .clone()
146
+ .filter(|(_, a)| *a < 0.)
147
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
148
+ {
149
+ use_style = found.0;
150
+ } else {
151
+ use_style = set[0].style;
152
+ }
153
+ }
154
+ // If the value of font-style is oblique...
155
+ else if let Some(angle) = oblique_angle(style) {
156
+ // and the requested angle is greater than or equal to 14deg
157
+ if angle >= OBLIQUE_THRESHOLD {
158
+ // oblique values greater than or equal to angle are checked in
159
+ // ascending order
160
+ if let Some(found) = oblique_fonts
161
+ .clone()
162
+ .filter(|(_, a)| *a >= angle)
163
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
164
+ {
165
+ use_style = found.0;
166
+ }
167
+ // followed by positive oblique values below angle in descending order
168
+ else if let Some(found) = oblique_fonts
169
+ .clone()
170
+ .filter(|(_, a)| *a > 0. && *a < angle)
171
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
172
+ {
173
+ use_style = found.0;
174
+ } else {
175
+ // If font-synthesis-style has the value auto, then for variable
176
+ // fonts with a slnt axis a match is created by setting the slnt
177
+ // value with the specified oblique value; otherwise, a fallback
178
+ // match is produced by geometric shearing to the specified
179
+ // oblique value.
180
+ if synthesize_style {
181
+ if set.iter().any(|f| f.has_slnt) {
182
+ _use_slnt = true;
183
+ } else {
184
+ use_style = if set.iter().any(|f| f.style == FontStyle::Normal) {
185
+ FontStyle::Normal
186
+ } else {
187
+ set[0].style
188
+ };
189
+ }
190
+ } else {
191
+ // Choose an italic style
192
+ if set.iter().any(|f| f.style == FontStyle::Italic) {
193
+ use_style = FontStyle::Italic;
194
+ }
195
+ // oblique values less than or equal to 0deg are checked in descending order
196
+ else if let Some(found) = oblique_fonts
197
+ .clone()
198
+ .filter(|(_, a)| *a <= 0.)
199
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
200
+ {
201
+ use_style = found.0;
202
+ } else {
203
+ use_style = set[0].style;
204
+ }
205
+ }
206
+ }
207
+ }
208
+ // if the requested angle is greater than or equal to 0deg
209
+ // and less than 14deg
210
+ else if angle >= 0. {
211
+ // positive oblique values below angle in descending order
212
+ if let Some(found) = oblique_fonts
213
+ .clone()
214
+ .filter(|(_, a)| *a > 0. && *a < angle)
215
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
216
+ {
217
+ use_style = found.0;
218
+ }
219
+ // followed by oblique values greater than or equal to angle in
220
+ // ascending order
221
+ else if let Some(found) = oblique_fonts
222
+ .clone()
223
+ .filter(|(_, a)| *a >= angle)
224
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
225
+ {
226
+ use_style = found.0;
227
+ } else {
228
+ // If font-synthesis-style has the value auto, then for variable
229
+ // fonts with a slnt axis a match is created by setting the slnt
230
+ // value with the specified oblique value; otherwise, a fallback
231
+ // match is produced by geometric shearing to the specified
232
+ // oblique value.
233
+ if synthesize_style {
234
+ if set.iter().any(|f| f.has_slnt) {
235
+ _use_slnt = true;
236
+ } else {
237
+ use_style = if set.iter().any(|f| f.style == FontStyle::Normal) {
238
+ FontStyle::Normal
239
+ } else {
240
+ set[0].style
241
+ };
242
+ }
243
+ } else {
244
+ // Choose an italic style
245
+ if set.iter().any(|f| f.style == FontStyle::Italic) {
246
+ use_style = FontStyle::Italic;
247
+ }
248
+ // oblique values less than or equal to 0deg are checked in descending order
249
+ else if let Some(found) = oblique_fonts
250
+ .clone()
251
+ .filter(|(_, a)| *a <= 0.)
252
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
253
+ {
254
+ use_style = found.0;
255
+ } else {
256
+ use_style = set[0].style;
257
+ }
258
+ }
259
+ }
260
+ }
261
+ // -14deg < angle < 0deg
262
+ else if angle > -OBLIQUE_THRESHOLD {
263
+ // negative oblique values above angle in ascending order
264
+ if let Some(found) = oblique_fonts
265
+ .clone()
266
+ .filter(|(_, a)| *a < 0. && *a > angle)
267
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
268
+ {
269
+ use_style = found.0;
270
+ }
271
+ // followed by oblique values less than or equal to angle in
272
+ // descending order
273
+ else if let Some(found) = oblique_fonts
274
+ .clone()
275
+ .filter(|(_, a)| *a <= angle)
276
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
277
+ {
278
+ use_style = found.0;
279
+ } else {
280
+ // If font-synthesis-style has the value auto, then for variable
281
+ // fonts with a slnt axis a match is created by setting the slnt
282
+ // value with the specified oblique value; otherwise, a fallback
283
+ // match is produced by geometric shearing to the specified
284
+ // oblique value.
285
+ if synthesize_style {
286
+ if set.iter().any(|f| f.has_slnt) {
287
+ _use_slnt = true;
288
+ } else {
289
+ use_style = if set.iter().any(|f| f.style == FontStyle::Normal) {
290
+ FontStyle::Normal
291
+ } else {
292
+ set[0].style
293
+ };
294
+ }
295
+ } else {
296
+ // Choose an italic style
297
+ if set.iter().any(|f| f.style == FontStyle::Italic) {
298
+ use_style = FontStyle::Italic;
299
+ }
300
+ // oblique values greater than or equal to 0deg are checked in ascending order
301
+ else if let Some(found) = oblique_fonts
302
+ .clone()
303
+ .filter(|(_, a)| *a >= 0.)
304
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
305
+ {
306
+ use_style = found.0;
307
+ } else {
308
+ use_style = set[0].style;
309
+ }
310
+ }
311
+ }
312
+ }
313
+ // angle < -14 deg
314
+ else {
315
+ // oblique values less than or equal to angle are checked in
316
+ // descending order
317
+ if let Some(found) = oblique_fonts
318
+ .clone()
319
+ .filter(|(_, a)| *a <= angle)
320
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
321
+ {
322
+ use_style = found.0;
323
+ }
324
+ // followed by negative oblique values above angle in ascending order
325
+ else if let Some(found) = oblique_fonts
326
+ .clone()
327
+ .filter(|(_, a)| *a < 0. && *a > angle)
328
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
329
+ {
330
+ use_style = found.0;
331
+ } else {
332
+ // If font-synthesis-style has the value auto, then for variable
333
+ // fonts with a slnt axis a match is created by setting the slnt
334
+ // value with the specified oblique value; otherwise, a fallback
335
+ // match is produced by geometric shearing to the specified
336
+ // oblique value.
337
+ if synthesize_style {
338
+ if set.iter().any(|f| f.has_slnt) {
339
+ _use_slnt = true;
340
+ } else {
341
+ use_style = if set.iter().any(|f| f.style == FontStyle::Normal) {
342
+ FontStyle::Normal
343
+ } else {
344
+ set[0].style
345
+ };
346
+ }
347
+ } else {
348
+ // Choose an italic style
349
+ if set.iter().any(|f| f.style == FontStyle::Italic) {
350
+ use_style = FontStyle::Italic;
351
+ }
352
+ // oblique values greater than or equal to 0deg are checked in ascending order
353
+ else if let Some(found) = oblique_fonts
354
+ .clone()
355
+ .filter(|(_, a)| *a >= 0.)
356
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
357
+ {
358
+ use_style = found.0;
359
+ } else {
360
+ use_style = set[0].style;
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
366
+ // If the value of font-style is normal...
367
+ else {
368
+ // oblique values greater than or equal to 0deg are checked in
369
+ // ascending order
370
+ if let Some(found) = oblique_fonts
371
+ .clone()
372
+ .filter(|(_, a)| *a >= 0.)
373
+ .min_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
374
+ {
375
+ use_style = found.0;
376
+ }
377
+ // followed by italic fonts
378
+ else if let Some(found) = set.iter().find(|f| f.style == FontStyle::Italic) {
379
+ use_style = found.style;
380
+ }
381
+ // followed by oblique values less than 0deg in descending order
382
+ else if let Some(found) = oblique_fonts
383
+ .clone()
384
+ .filter(|(_, a)| *a < 0.)
385
+ .max_by(|x, y| x.1.partial_cmp(&y.1).unwrap_or(Less))
386
+ {
387
+ use_style = found.0;
388
+ } else {
389
+ use_style = set[0].style;
390
+ }
391
+ }
392
+ }
393
+ set.retain(|f| f.style == use_style);
394
+ // font-weight is matched next:
395
+ if let Some(f) = set.iter().find(|f| f.weight == weight) {
396
+ return Some(f.index);
397
+ } else {
398
+ // If the desired weight is inclusively between 400 and 500...
399
+ if (400.0..=500.0).contains(&weight) {
400
+ // weights greater than or equal to the target weight are checked in ascending
401
+ // order until 500 is hit and checked
402
+ if let Some(found) = set
403
+ .iter()
404
+ .enumerate()
405
+ .filter(|f| f.1.weight >= weight && f.1.weight <= 500.0)
406
+ .min_by(|x, y| x.1.weight.partial_cmp(&y.1.weight).unwrap_or(Less))
407
+ {
408
+ return Some(found.1.index);
409
+ }
410
+ // followed by weights less than the target weight in descending order
411
+ if let Some(found) = set
412
+ .iter()
413
+ .enumerate()
414
+ .filter(|f| f.1.weight < weight)
415
+ .max_by(|x, y| x.1.weight.partial_cmp(&y.1.weight).unwrap_or(Less))
416
+ {
417
+ return Some(found.1.index);
418
+ }
419
+ // followed by weights greater than 500, until a match is found.
420
+ if let Some(found) = set
421
+ .iter()
422
+ .enumerate()
423
+ .filter(|f| f.1.weight > 500.0)
424
+ .min_by(|x, y| x.1.weight.partial_cmp(&y.1.weight).unwrap_or(Less))
425
+ {
426
+ return Some(found.1.index);
427
+ }
428
+ }
429
+ // If the desired weight is less than 400...
430
+ else if weight < 400.0 {
431
+ // weights less than or equal to the target weight are checked in descending
432
+ if let Some(found) = set
433
+ .iter()
434
+ .filter(|f| f.weight <= weight)
435
+ .max_by(|x, y| x.weight.partial_cmp(&y.weight).unwrap_or(Less))
436
+ {
437
+ return Some(found.index);
438
+ }
439
+ // followed by weights greater than the target weight in ascending order
440
+ if let Some(found) = set
441
+ .iter()
442
+ .filter(|f| f.weight > weight)
443
+ .min_by(|x, y| x.weight.partial_cmp(&y.weight).unwrap_or(Less))
444
+ {
445
+ return Some(found.index);
446
+ }
447
+ }
448
+ // If the desired weight is greater than 500...
449
+ else {
450
+ // weights greater than or equal to the target weight are checked in ascending
451
+ if let Some(found) = set
452
+ .iter()
453
+ .filter(|f| f.weight >= weight)
454
+ .min_by(|x, y| x.weight.partial_cmp(&y.weight).unwrap_or(Less))
455
+ {
456
+ return Some(found.index);
457
+ }
458
+ // followed by weights less than the target weight in descending order
459
+ if let Some(found) = set
460
+ .iter()
461
+ .filter(|f| f.weight < weight)
462
+ .max_by(|x, y| x.weight.partial_cmp(&y.weight).unwrap_or(Less))
463
+ {
464
+ return Some(found.index);
465
+ }
466
+ }
467
+ }
468
+ None
469
+ }
470
+
471
+ fn oblique_angle(style: FontStyle) -> Option<f32> {
472
+ match style {
473
+ FontStyle::Oblique(angle) => Some(angle.unwrap_or(DEFAULT_OBLIQUE_ANGLE)),
474
+ _ => None,
475
+ }
476
+ }
477
+
478
+ fn oblique_style(style: FontStyle) -> Option<(FontStyle, f32)> {
479
+ match style {
480
+ FontStyle::Oblique(angle) => Some((style, angle.unwrap_or(DEFAULT_OBLIQUE_ANGLE))),
481
+ _ => None,
482
+ }
483
+ }