blitz-pdf 0.1.6 → 0.1.7

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 (39) hide show
  1. package/README.md +23 -6
  2. package/THIRD_PARTY_NOTICES.md +18 -0
  3. package/dist/types.d.ts +2 -1
  4. package/dist/types.d.ts.map +1 -1
  5. package/licenses/Noto-OFL.txt +94 -0
  6. package/licenses/NotoCJK-OFL.txt +92 -0
  7. package/licenses/NotoEmoji-OFL.txt +93 -0
  8. package/licenses/Unifont-OFL.txt +90 -0
  9. package/licenses/font-sources.json +106 -0
  10. package/package.json +1 -1
  11. package/src/types.ts +2 -1
  12. package/vendor/README.md +19 -0
  13. package/vendor/fontique/Cargo.toml +194 -0
  14. package/vendor/fontique/LICENSE-APACHE +201 -0
  15. package/vendor/fontique/LICENSE-MIT +25 -0
  16. package/vendor/fontique/README.md +54 -0
  17. package/vendor/fontique/examples/generic_families.rs +37 -0
  18. package/vendor/fontique/src/attributes.rs +42 -0
  19. package/vendor/fontique/src/backend/android.rs +218 -0
  20. package/vendor/fontique/src/backend/coretext.rs +206 -0
  21. package/vendor/fontique/src/backend/dwrite.rs +395 -0
  22. package/vendor/fontique/src/backend/fontconfig.rs +844 -0
  23. package/vendor/fontique/src/backend/mod.rs +75 -0
  24. package/vendor/fontique/src/charmap.rs +127 -0
  25. package/vendor/fontique/src/collection/mod.rs +1016 -0
  26. package/vendor/fontique/src/collection/query.rs +392 -0
  27. package/vendor/fontique/src/fallback.rs +293 -0
  28. package/vendor/fontique/src/family.rs +111 -0
  29. package/vendor/fontique/src/family_name.rs +153 -0
  30. package/vendor/fontique/src/font.rs +562 -0
  31. package/vendor/fontique/src/generic.rs +36 -0
  32. package/vendor/fontique/src/impl_fontconfig.rs +159 -0
  33. package/vendor/fontique/src/lib.rs +87 -0
  34. package/vendor/fontique/src/matching.rs +483 -0
  35. package/vendor/fontique/src/scan.rs +312 -0
  36. package/vendor/fontique/src/script.rs +271 -0
  37. package/vendor/fontique/src/source.rs +108 -0
  38. package/vendor/fontique/src/source_cache.rs +254 -0
  39. package/wasm/blitz_pdf_bg.wasm +0 -0
@@ -0,0 +1,395 @@
1
+ // Copyright 2024 the Parley Authors
2
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
3
+
4
+ use alloc::string::String;
5
+ use alloc::sync::Arc;
6
+ use alloc::{vec, vec::Vec};
7
+ use hashbrown::HashMap;
8
+ use std::{
9
+ ffi::{OsString, c_void},
10
+ os::windows::ffi::OsStringExt,
11
+ path::PathBuf,
12
+ };
13
+ use windows::Win32::Foundation::STATUS_NOT_FOUND;
14
+ use windows::{
15
+ Win32::Graphics::DirectWrite::{
16
+ DWRITE_FACTORY_TYPE_SHARED, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL,
17
+ DWRITE_FONT_WEIGHT_REGULAR, DWRITE_READING_DIRECTION,
18
+ DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, DWriteCreateFactory, IDWriteFactory,
19
+ IDWriteFactory2, IDWriteFont, IDWriteFontCollection, IDWriteFontFace, IDWriteFontFallback,
20
+ IDWriteFontFamily, IDWriteFontFile, IDWriteLocalFontFileLoader, IDWriteNumberSubstitution,
21
+ IDWriteTextAnalysisSource, IDWriteTextAnalysisSource_Impl,
22
+ },
23
+ core::{Interface, OutRef, PCWSTR, implement},
24
+ };
25
+
26
+ use super::{
27
+ FallbackKey, FamilyId, FamilyInfo, FamilyNameMap, FontInfo, GenericFamily, GenericFamilyMap,
28
+ ScriptExt, SourcePathMap,
29
+ };
30
+
31
+ const DEFAULT_GENERIC_FAMILIES: &[(GenericFamily, &[&str])] = &[
32
+ (GenericFamily::Serif, &["Times New Roman"]),
33
+ (GenericFamily::SansSerif, &["Arial"]),
34
+ (GenericFamily::Monospace, &["Consolas"]),
35
+ (GenericFamily::Cursive, &["Comic Sans MS"]),
36
+ (GenericFamily::Fantasy, &["Impact"]),
37
+ (GenericFamily::SystemUi, &["Segoe UI"]),
38
+ (GenericFamily::Emoji, &["Segoe UI Emoji"]),
39
+ (GenericFamily::Math, &["Cambria Math"]),
40
+ (GenericFamily::FangSong, &["FangSong"]),
41
+ ];
42
+
43
+ /// Raw access to the collection of local system fonts.
44
+ pub(crate) struct SystemFonts {
45
+ pub(crate) name_map: Arc<FamilyNameMap>,
46
+ pub(crate) generic_families: Arc<GenericFamilyMap>,
47
+ source_cache: SourcePathMap,
48
+ family_map: HashMap<FamilyId, Option<FamilyInfo>>,
49
+ dwrite_fonts: DWriteSystemFonts,
50
+ }
51
+
52
+ impl SystemFonts {
53
+ pub(crate) fn new() -> Self {
54
+ let dwrite_fonts = DWriteSystemFonts::new(false).unwrap();
55
+ let mut name_map = FamilyNameMap::default();
56
+ for family in dwrite_fonts.families() {
57
+ if let Some(names) = family.names() {
58
+ let [first_name, other_names @ ..] = names.as_slice() else {
59
+ continue;
60
+ };
61
+ let id = name_map.get_or_insert(first_name).id();
62
+ for other_name in other_names {
63
+ name_map.add_alias(id, other_name);
64
+ }
65
+ }
66
+ }
67
+ let mut generic_families = GenericFamilyMap::default();
68
+ for (family, names) in DEFAULT_GENERIC_FAMILIES {
69
+ generic_families.set(
70
+ *family,
71
+ names
72
+ .iter()
73
+ .filter_map(|name| name_map.get(name))
74
+ .map(|name| name.id()),
75
+ );
76
+ }
77
+ Self {
78
+ name_map: Arc::new(name_map),
79
+ generic_families: Arc::new(generic_families),
80
+ source_cache: SourcePathMap::default(),
81
+ family_map: HashMap::default(),
82
+ dwrite_fonts,
83
+ }
84
+ }
85
+
86
+ pub(crate) fn family(&mut self, id: FamilyId) -> Option<FamilyInfo> {
87
+ match self.family_map.get(&id) {
88
+ Some(Some(family)) => return Some(family.clone()),
89
+ Some(None) => return None,
90
+ _ => {}
91
+ }
92
+ let name = self.name_map.get_by_id(id)?;
93
+ let mut fonts = smallvec::SmallVec::<[FontInfo; 4]>::default();
94
+ if let Some(family) = self.dwrite_fonts.family_by_name(name.name()) {
95
+ for font in family.fonts() {
96
+ if let Some(font) = FontInfo::from_dwrite(&font, &mut self.source_cache)
97
+ && !fonts
98
+ .iter()
99
+ .any(|f| f.source().id() == font.source().id() && f.index() == font.index())
100
+ {
101
+ fonts.push(font);
102
+ }
103
+ }
104
+ if !fonts.is_empty() {
105
+ let family = FamilyInfo::new(name.clone(), fonts);
106
+ self.family_map.insert(id, Some(family.clone()));
107
+ return Some(family);
108
+ }
109
+ }
110
+ self.family_map.insert(id, None);
111
+ None
112
+ }
113
+
114
+ pub(crate) fn fallback(&mut self, key: impl Into<FallbackKey>) -> Option<FamilyId> {
115
+ // DirectWrite does not have a function to get the default font for a script and locale pair
116
+ // so here we provide a sample of the intended script instead.
117
+ let key = key.into();
118
+ let text = key.script().sample()?;
119
+ let locale = key.locale_str();
120
+ let family_name = self.dwrite_fonts.family_name_for_text(text, locale)?;
121
+ self.name_map.get(&family_name).map(|name| name.id())
122
+ }
123
+ }
124
+
125
+ impl FontInfo {
126
+ fn from_dwrite(font: &DWriteFont, paths: &mut SourcePathMap) -> Option<Self> {
127
+ let path = font.file_path()?;
128
+ let index = font.index();
129
+ let data = paths.get_or_insert(&path);
130
+ Self::from_source(data, index)
131
+ }
132
+ }
133
+
134
+ struct DWriteSystemFonts {
135
+ collection: IDWriteFontCollection,
136
+ fallback: Option<IDWriteFontFallback>,
137
+ map_buf: Vec<u16>,
138
+ }
139
+
140
+ impl DWriteSystemFonts {
141
+ fn new(update: bool) -> windows_core::Result<Self> {
142
+ unsafe {
143
+ let factory = DWriteCreateFactory::<IDWriteFactory>(DWRITE_FACTORY_TYPE_SHARED)?;
144
+ let mut collection: Option<IDWriteFontCollection> = None;
145
+ factory.GetSystemFontCollection(&mut collection, update)?;
146
+ let collection = collection.ok_or_else(|| {
147
+ windows_core::Error::new(STATUS_NOT_FOUND.to_hresult(), "no collection")
148
+ })?;
149
+ let fallback = factory
150
+ .cast::<IDWriteFactory2>()
151
+ .and_then(|f| f.GetSystemFontFallback())
152
+ .ok();
153
+ Ok(Self {
154
+ collection,
155
+ fallback,
156
+ map_buf: vec![],
157
+ })
158
+ }
159
+ }
160
+
161
+ fn get(&self, index: u32) -> Option<DWriteFontFamily> {
162
+ unsafe {
163
+ self.collection
164
+ .GetFontFamily(index)
165
+ .ok()
166
+ .map(DWriteFontFamily)
167
+ }
168
+ }
169
+
170
+ fn family_by_name(&self, name: &str) -> Option<DWriteFontFamily> {
171
+ let mut index = 0;
172
+ #[expect(
173
+ clippy::default_trait_access,
174
+ reason = "The type this resolves to doesn't seem to be nameable."
175
+ )]
176
+ // The documentation for `FindFamilyName` seems to be:
177
+ // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Graphics/DirectWrite/trait.IDWriteFontCollection_Impl.html#tymethod.FindFamilyName
178
+ // And we need a value of type BOOL, which you'll notice is allegedly not actually public (i.e. it has no link).
179
+ let mut exists = Default::default();
180
+ let mut name_buf = smallvec::SmallVec::<[u16; 128]>::default();
181
+ name_buf.extend(name.encode_utf16());
182
+ name_buf.push(0);
183
+ unsafe {
184
+ self.collection
185
+ .FindFamilyName(PCWSTR::from_raw(name_buf.as_ptr()), &mut index, &mut exists)
186
+ .ok()?;
187
+ }
188
+ self.get(index)
189
+ }
190
+
191
+ fn families(&self) -> impl Iterator<Item = DWriteFontFamily> {
192
+ let this = self.collection.clone();
193
+ unsafe {
194
+ let count = this.GetFontFamilyCount();
195
+ (0..count).filter_map(move |index| this.GetFontFamily(index).ok().map(DWriteFontFamily))
196
+ }
197
+ }
198
+
199
+ fn family_name_for_text(&mut self, text: &str, locale: Option<&str>) -> Option<String> {
200
+ self.map_buf.clear();
201
+ self.map_buf.extend(text.encode_utf16());
202
+ let text_len = self.map_buf.len();
203
+ if let Some(locale) = locale {
204
+ self.map_buf.extend(locale.encode_utf16());
205
+ }
206
+ self.map_buf.push(0);
207
+ let (text, locale) = self.map_buf.split_at(text_len);
208
+ let source: IDWriteTextAnalysisSource = TextSource { text, locale }.into();
209
+ unsafe {
210
+ let mut cur_offset = 0;
211
+ while cur_offset < text_len {
212
+ if let Some(fallback) = self.fallback.as_ref() {
213
+ let mut mapped_len = 0;
214
+ let mut mapped_font = None;
215
+ if fallback
216
+ .MapCharacters(
217
+ &source,
218
+ cur_offset as u32,
219
+ (text_len - cur_offset) as u32,
220
+ &self.collection,
221
+ None,
222
+ DWRITE_FONT_WEIGHT_REGULAR,
223
+ DWRITE_FONT_STYLE_NORMAL,
224
+ DWRITE_FONT_STRETCH_NORMAL,
225
+ &mut mapped_len,
226
+ &mut mapped_font,
227
+ &mut 1.0,
228
+ )
229
+ .is_ok()
230
+ && let Some(font) = mapped_font
231
+ {
232
+ let family = font.GetFontFamily().ok()?;
233
+ let names = family.GetFamilyNames().ok()?;
234
+ let name_len = names.GetStringLength(0).ok()? as usize;
235
+ let mut name_buf = smallvec::SmallVec::<[u16; 128]>::default();
236
+ name_buf.resize(name_len + 1, 0);
237
+ names.GetString(0, &mut name_buf).ok()?;
238
+ name_buf.pop();
239
+ return Some(String::from_utf16_lossy(&name_buf));
240
+ }
241
+ }
242
+ cur_offset += 1;
243
+ }
244
+ }
245
+ None
246
+ }
247
+ }
248
+
249
+ #[derive(Clone)]
250
+ struct DWriteFontFamily(IDWriteFontFamily);
251
+
252
+ impl DWriteFontFamily {
253
+ fn names(&self) -> Option<Vec<String>> {
254
+ let mut names = vec![];
255
+ unsafe {
256
+ let family_names = self.0.GetFamilyNames().ok()?;
257
+ let count = family_names.GetCount();
258
+ let mut buf = vec![];
259
+ for i in 0..count {
260
+ let Ok(len) = family_names.GetStringLength(i) else {
261
+ continue;
262
+ };
263
+ buf.clear();
264
+ buf.resize(len as usize + 1, 0);
265
+ if family_names.GetString(i, &mut buf).is_err() {
266
+ continue;
267
+ }
268
+ buf.pop();
269
+ names.push(String::from_utf16_lossy(&buf));
270
+ }
271
+ }
272
+ Some(names)
273
+ }
274
+
275
+ fn fonts(&self) -> impl Iterator<Item = DWriteFont> {
276
+ let this = self.0.clone();
277
+ unsafe {
278
+ let count = self.0.GetFontCount();
279
+ (0..count).filter_map(move |index| {
280
+ let font = this.GetFont(index).ok()?;
281
+ // We don't want fonts with simulations
282
+ if font.GetSimulations().0 != 0 {
283
+ return None;
284
+ }
285
+ DWriteFont::new(&font)
286
+ })
287
+ }
288
+ }
289
+ }
290
+
291
+ // Note, this is a font face. We don't care about the font.
292
+ #[derive(Clone)]
293
+ struct DWriteFont(IDWriteFontFace);
294
+
295
+ impl DWriteFont {
296
+ fn new(font: &IDWriteFont) -> Option<Self> {
297
+ unsafe { Some(Self(font.CreateFontFace().ok()?)) }
298
+ }
299
+
300
+ fn file_path(&self) -> Option<PathBuf> {
301
+ unsafe {
302
+ // We only care about fonts with a single file.
303
+ let mut file: Option<IDWriteFontFile> = None;
304
+ self.0.GetFiles(&mut 1, Some(&mut file)).ok()?;
305
+ let file = file?;
306
+ // Now the ugly stuff...
307
+ let mut ref_key: *mut c_void = core::ptr::null_mut();
308
+ let mut ref_key_size: u32 = 0;
309
+ file.GetReferenceKey(&mut ref_key, &mut ref_key_size).ok()?;
310
+ let loader = file.GetLoader().ok()?;
311
+ let local_loader: IDWriteLocalFontFileLoader = loader.cast().ok()?;
312
+ let file_path_len = local_loader
313
+ .GetFilePathLengthFromKey(ref_key, ref_key_size)
314
+ .ok()?;
315
+ let mut file_path_buf = vec![0; file_path_len as usize + 1];
316
+ local_loader
317
+ .GetFilePathFromKey(ref_key, ref_key_size, &mut file_path_buf)
318
+ .ok()?;
319
+ if let Some(&0) = file_path_buf.last() {
320
+ file_path_buf.pop();
321
+ }
322
+ Some(PathBuf::from(OsString::from_wide(&file_path_buf)))
323
+ }
324
+ }
325
+
326
+ fn index(&self) -> u32 {
327
+ unsafe { self.0.GetIndex() }
328
+ }
329
+ }
330
+
331
+ #[implement(IDWriteTextAnalysisSource)]
332
+ struct TextSource<'a> {
333
+ text: &'a [u16],
334
+ locale: &'a [u16],
335
+ }
336
+
337
+ impl IDWriteTextAnalysisSource_Impl for TextSource_Impl<'_> {
338
+ fn GetLocaleName(
339
+ &self,
340
+ textposition: u32,
341
+ textlength: *mut u32,
342
+ localename: *mut *mut u16,
343
+ ) -> windows::core::Result<()> {
344
+ unsafe {
345
+ *textlength = (self.text.len() as u32).saturating_sub(textposition);
346
+ *localename = core::mem::transmute::<*const u16, *mut u16>(self.locale.as_ptr());
347
+ }
348
+ Ok(())
349
+ }
350
+
351
+ fn GetNumberSubstitution(
352
+ &self,
353
+ textposition: u32,
354
+ textlength: *mut u32,
355
+ numbersubstitution: OutRef<'_, IDWriteNumberSubstitution>,
356
+ ) -> windows::core::Result<()> {
357
+ numbersubstitution.write(None)?;
358
+ unsafe {
359
+ *textlength = (self.text.len() as u32).saturating_sub(textposition);
360
+ }
361
+ Ok(())
362
+ }
363
+
364
+ fn GetParagraphReadingDirection(&self) -> DWRITE_READING_DIRECTION {
365
+ DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
366
+ }
367
+
368
+ fn GetTextAtPosition(
369
+ &self,
370
+ textposition: u32,
371
+ textstring: *mut *mut u16,
372
+ textlength: *mut u32,
373
+ ) -> windows::core::Result<()> {
374
+ unsafe {
375
+ let text = self.text.get(textposition as usize..).unwrap_or_default();
376
+ *textlength = text.len() as _;
377
+ *textstring = core::mem::transmute::<*const u16, *mut u16>(text.as_ptr());
378
+ }
379
+ Ok(())
380
+ }
381
+
382
+ fn GetTextBeforePosition(
383
+ &self,
384
+ textposition: u32,
385
+ textstring: *mut *mut u16,
386
+ textlength: *mut u32,
387
+ ) -> windows::core::Result<()> {
388
+ unsafe {
389
+ let text = self.text.get(..textposition as usize).unwrap_or_default();
390
+ *textlength = text.len() as _;
391
+ *textstring = core::mem::transmute::<*const u16, *mut u16>(text.as_ptr());
392
+ }
393
+ Ok(())
394
+ }
395
+ }