1use super::index_data::*;
4use super::types::*;
5use super::{
6 fallback::Fallbacks,
7 types::{FamilyId, GenericFamily},
8};
9#[cfg(feature = "emacs")]
10use crate::emacs::FontSpec;
11use crate::util::{
12 fxhash::FxHashMap,
13 string::{LowercaseString, SmallString},
14};
15#[cfg(feature = "emacs")]
16use fancy_regex::Regex;
17#[cfg(feature = "emacs")]
18use log::warn;
19#[cfg(feature = "emacs")]
20use std::collections::HashSet;
21use std::path::Path;
22use swash::text::{Cjk, Script};
23use swash::Tag;
24#[cfg(feature = "emacs")]
25use swash::{text::Language, Stretch, Style, Weight};
26use swash::{Attributes, CacheKey};
27
28pub type RequestedAttributes = Attributes;
31
32#[derive(Default)]
33pub struct BaseIndex {
34 pub family_map: FxHashMap<SmallString, FamilyId>,
35 pub fonts: Vec<FontData>,
36 pub sources: Vec<SourceData>,
37}
38
39pub struct StaticIndex {
40 pub base: BaseIndex,
41 pub families: Vec<FamilyData>,
42 pub script_map: FxHashMap<Script, Fallbacks>,
43 pub script_tag_map: FxHashMap<Tag, Vec<FamilyId>>,
44 pub language_tag_map: FxHashMap<Tag, Vec<FamilyId>>,
45 #[cfg(feature = "emacs")]
46 pub emacs_charset_map: FxHashMap<SmallString, Vec<FamilyId>>,
47 #[cfg(feature = "emacs")]
48 pub emacs_script_map: FxHashMap<SmallString, Vec<FamilyId>>,
49 pub cjk: [Fallbacks; 5],
50 pub generic: [Option<FamilyId>; 13],
51}
52
53impl Default for StaticIndex {
54 fn default() -> Self {
55 let fallbacks = Fallbacks::new();
56 Self {
57 base: BaseIndex::default(),
58 families: Vec::new(),
59 script_map: Default::default(),
60 script_tag_map: Default::default(),
61 language_tag_map: Default::default(),
62 #[cfg(feature = "emacs")]
63 emacs_charset_map: Default::default(),
64 #[cfg(feature = "emacs")]
65 emacs_script_map: Default::default(),
66 cjk: [fallbacks; 5],
67 generic: [None; 13],
68 }
69 }
70}
71
72impl StaticIndex {
73 pub fn setup_default_fallbacks(&mut self) {
74 use super::system::*;
75 use Cjk::*;
76 use Script::*;
77 match OS {
78 Os::Windows => {
79 self.cjk[Simplified as usize] =
81 self.find_fallbacks(&["microsoft yahei", "simsun", "simsun-extb"]);
82 self.cjk[Traditional as usize] =
84 self.find_fallbacks(&["microsoft jhenghei", "pmingliu", "pmingliu-extb"]);
85 self.cjk[Cjk::None as usize] = self.cjk[Traditional as usize];
86 self.cjk[Japanese as usize] = self.find_fallbacks(&[
88 "meiryo",
89 "yu gothic",
90 "microsoft yahei",
91 "simsun",
92 "simsun-extb",
93 ]);
94 self.cjk[Korean as usize] = self.find_fallbacks(&[
96 "malgun gothic",
97 "gulim",
98 "microsoft yahei",
99 "simsun",
100 "simsun-extb",
101 ]);
102 self.map_script(Latin, &["times new roman"]);
103 self.map_script(Arabic, &["tahoma", "segoe ui"]);
104 self.map_script(Armenian, &["segoe ui", "sylfaen"]);
105 self.map_script(Bengali, &["nirmala ui", "vrinda"]);
106 self.map_script(Brahmi, &["segoe ui historic"]);
107 self.map_script(Braille, &["segoe ui symbol"]);
108 self.map_script(Buginese, &["leelawadee ui"]);
109 self.map_script(CanadianAboriginal, &["gadugi", "euphemia"]);
110 self.map_script(Carian, &["segoe ui historic"]);
111 self.map_script(Devanagari, &["nirmala ui", "mangal"]);
112 self.map_script(Hebrew, &["david", "segoe ui", "calibri"]);
113 self.map_script(Hangul, &["malgun gothic", "gulim"]);
114 self.map_script(Myanmar, &["myanmar text"]);
115 self.map_script(Malayalam, &["nirmala ui", "kartika"]);
116 self.map_script(Han, &["microsoft yahei", "simsun", "simsun-extb"]);
117 self.map_script(
118 Hiragana,
119 &["meiryo", "yu gothic", "ms pgothic", "microsoft yahei"],
120 );
121 self.map_script(
122 Katakana,
123 &["meiryo", "yu gothic", "ms pgothic", "microsoft yahei"],
124 );
125 self.map_script(Kharoshthi, &["segoe ui historic"]);
126 self.map_script(
127 Khmer,
128 &[
129 "leelawadee ui",
130 "khmer ui",
131 "khmer os",
132 "moolboran",
133 "daunpenh",
134 ],
135 );
136 self.map_script(
137 Lao,
138 &[
139 "leelawadee ui",
140 "lao ui",
141 "dokchampa",
142 "saysettha ot",
143 "phetsarath ot",
144 "code2000",
145 ],
146 );
147 self.map_script(Lisu, &["segoe ui"]);
148 self.map_script(
149 Syriac,
150 &["estrangelo edessa", "estrangelo nisibin", "code2000"],
151 );
152 self.map_script(Thai, &["tahoma", "leelawadee ui", "leelawadee"]);
153 self.map_script(
154 Tibetan,
155 &["microsoft himalaya", "jomolhari", "tibetan machine uni"],
156 );
157 self.map_script(Vai, &["ebrima"]);
158 self.map_script(Yi, &["microsoft yi baiti", "nuosu sil", "code2000"]);
159 }
160 Os::MacOs => {
161 self.cjk[Simplified as usize] = self.find_fallbacks(&["pingfang sc"]);
163 self.cjk[Traditional as usize] = self.find_fallbacks(&["pingfang tc"]);
165 self.cjk[Cjk::None as usize] = self.cjk[Traditional as usize];
166 self.cjk[Japanese as usize] =
168 self.find_fallbacks(&["hiragino kaku gothic pron w3"]);
169 self.cjk[Korean as usize] = self.find_fallbacks(&["apple sd gothic neo"]);
171 self.map_script(Latin, &["times", "times new roman"]);
172 self.map_script(Arabic, &["geeza pro"]);
173 self.map_script(
174 Devanagari,
175 &[
176 "itf devanagari",
177 "kohinoor devanagari",
178 "devanagari sangam mn",
179 "devanagari mt",
180 ],
181 );
182 self.map_script(Bengali, &[]);
183 self.map_script(Myanmar, &["noto sans myanmar", "myanmar mn"]);
184 self.map_script(Malayalam, &["malayalam mn"]);
185 self.map_script(Hebrew, &["lucida grande", "arial hebrew"]);
186 }
187 _ => {
188 self.map_script(
189 Latin,
190 &[
191 "liberation sans",
192 "dejavu sans",
193 "ubuntu",
194 "source sans pro",
195 ],
196 );
197 self.map_script(Arabic, &["noto sans arabic"]);
198 self.map_script(Hebrew, &["noto sans hebrew", "noto serif hebrew"]);
199 self.map_script(Bengali, &["noto sans bengali", "noto serif bengali"]);
200 self.map_script(
201 Devanagari,
202 &["noto sans devanagari", "noto serif devanagari"],
203 );
204 self.map_script(Malayalam, &["noto sans malayalam", "noto serif malayalam"]);
205 self.map_script(Myanmar, &["noto sans myanmar", "noto serif myanmar"]);
206 }
207 }
208 }
209
210 pub fn setup_default_generic(&mut self) {
211 use super::system::*;
212 use GenericFamily::*;
213 match OS {
214 Os::Windows => {
215 self.generic[SansSerif as usize] = self.find_family(&["arial"]);
216 self.generic[Serif as usize] = self.find_family(&["times new roman"]);
217 self.generic[Monospace as usize] = self.find_family(&["courier new"]);
218 self.generic[Fantasy as usize] = self.find_family(&["impact"]);
219 self.generic[Cursive as usize] = self.find_family(&["comic sans ms"]);
220 self.generic[SystemUI as usize] = self.find_family(&["segoe ui"]);
221 self.generic[Emoji as usize] = self.find_family(&["segoe ui emoji"]);
222 }
223 Os::MacOs => {
224 self.generic[SansSerif as usize] = self.find_family(&["helvetica"]);
225 self.generic[Serif as usize] = self.find_family(&["times"]);
226 self.generic[Monospace as usize] = self.find_family(&["courier"]);
227 self.generic[Fantasy as usize] = self.find_family(&["papyrus"]);
228 self.generic[Cursive as usize] = self.find_family(&["apple chancery"]);
229 self.generic[SystemUI as usize] = self.find_family(&["system font", "helvetica"]);
230 self.generic[Emoji as usize] = self.find_family(&["apple color emoji"]);
231 }
232 Os::Ios => {
233 self.generic[SansSerif as usize] = self.find_family(&["helvetica"]);
234 self.generic[Serif as usize] = self.find_family(&["times new roman"]);
235 self.generic[Monospace as usize] = self.find_family(&["courier"]);
236 self.generic[Fantasy as usize] = self.find_family(&["papyrus"]);
237 self.generic[Cursive as usize] = self.find_family(&["snell roundhand"]);
238 self.generic[SystemUI as usize] = self.find_family(&["system font", "helvetica"]);
239 self.generic[Emoji as usize] = self.find_family(&["apple color emoji"]);
240 }
241 Os::Android => {
242 self.generic[SansSerif as usize] = self.find_family(&["roboto"]);
243 self.generic[Serif as usize] = self.find_family(&["noto serif", "droid serif"]);
244 self.generic[Monospace as usize] = self.find_family(&["droid sans mono"]);
245 self.generic[Fantasy as usize] = self.find_family(&["noto serif"]);
246 self.generic[Cursive as usize] = self.find_family(&["dancing script"]);
247 self.generic[SystemUI as usize] = self.find_family(&["roboto"]);
248 self.generic[Emoji as usize] = self.find_family(&["noto color emoji"]);
249 }
250 Os::Unix | Os::Other => {
251 self.generic[SansSerif as usize] =
252 self.find_family(&["liberation sans", "dejavu sans"]);
253 self.generic[Serif as usize] = self.find_family(&[
254 "liberation serif",
255 "dejavu serif",
256 "noto serif",
257 "times new roman",
258 ]);
259 self.generic[Monospace as usize] = self.find_family(&["dejavu sans mono"]);
260 self.generic[Fantasy as usize] =
261 self.find_family(&["liberation serif", "dejavu serif"]);
262 self.generic[Cursive as usize] =
263 self.find_family(&["liberation serif", "dejavu serif"]);
264 self.generic[SystemUI as usize] =
265 self.find_family(&["liberation sans", "dejavu sans"]);
266 self.generic[Emoji as usize] = self.find_family(&["noto color emoji", "emoji one"]);
267 }
268 }
269 }
270
271 pub fn emoji_family(&self) -> Option<FamilyId> {
272 self.generic[GenericFamily::Emoji as usize]
273 }
274
275 pub fn fallbacks(&self, script: Script, cjk: Cjk) -> &[FamilyId] {
276 if script == Script::Han {
277 self.cjk[cjk as usize].get()
278 } else {
279 self.script_map.get(&script).map(|f| f.get()).unwrap_or(&[])
280 }
281 }
282
283 fn map_script(&mut self, script: Script, families: &[&str]) {
284 let fallbacks = self.find_fallbacks(families);
285 if fallbacks.len() != 0 {
286 self.script_map.insert(script, fallbacks);
287 }
288 }
289
290 fn find_family(&self, families: &[&str]) -> Option<FamilyId> {
291 for family in families {
292 if let Some(id) = self.base.family_map.get(*family) {
293 return Some(*id);
294 }
295 }
296 None
297 }
298
299 fn find_fallbacks(&self, families: &[&str]) -> Fallbacks {
300 let mut fallbacks = Fallbacks::new();
301 for family in families {
302 if let Some(id) = self.base.family_map.get(*family) {
303 if !fallbacks.push(*id) {
304 break;
305 }
306 }
307 }
308 fallbacks
309 }
310}
311
312impl StaticIndex {
313 pub fn query<'a>(
316 &'a self,
317 family: impl Into<FamilyKey<'a>>,
318 attributes: impl Into<Attributes>,
319 ) -> Option<FontEntry<'a>> {
320 let family = self.family_by_key(family)?;
321 let attrs = attributes.into();
322 let font_id = family.data.query(attrs)?;
323 let data = self.base.fonts.get(font_id.to_usize())?;
324 Some(FontEntry {
325 index: &self.base,
326 family: family.data,
327 data,
328 })
329 }
330
331 #[cfg(feature = "emacs")]
334 pub fn list<'a>(&'a self, spec: FontSpec) -> Vec<FontEntry<'a>> {
335 let filter = |family: FamilyKey<'a>,
336 stretch: Option<Stretch>,
337 weight: Option<Weight>,
338 style: Option<Style>,
339 otf: Option<OpentypeSpec>| {
340 let family = self.family_by_key(family);
341 if family.is_none() {
342 return vec![];
343 }
344 let family = family.unwrap();
345 let fonts = family.data.list(stretch, weight, style, otf);
346 let fonts = fonts
347 .iter()
348 .filter_map(|font_id| {
349 if let Some(data) = self.base.fonts.get(font_id.to_usize()) {
350 return Some(FontEntry {
351 index: &self.base,
352 family: family.data,
353 data,
354 });
355 }
356 None
357 })
358 .collect();
359 return fonts;
360 };
361
362 let FontSpec {
364 width,
365 weight,
366 slant,
367 spacing,
368 otf,
369 ..
370 } = spec.clone();
371
372 if let Some(_) = spacing {
373 warn!("spacing is not yet supported");
374 }
375 self.families_by_spec(spec)
376 .iter()
377 .map(|family_id| FamilyKey::from(*family_id))
378 .map(|key| filter(key, width, weight, slant, otf.clone()))
379 .flatten()
380 .collect()
381 }
382
383 #[cfg(feature = "emacs")]
384 pub fn match_<'a>(&'a self, spec: FontSpec) -> Option<FontEntry<'a>> {
385 let FontSpec {
387 width,
388 weight,
389 slant,
390 spacing,
391 otf,
392 ..
393 } = spec.clone();
394 if let Some(_) = spacing {
395 warn!("spacing is not yet supported");
396 }
397 let attrs = Attributes::new(
398 width.unwrap_or(Stretch::NORMAL),
399 weight.unwrap_or(Weight::NORMAL),
400 slant.unwrap_or(Style::Normal),
401 );
402
403 let query = |family: FamilyId, attributes: Attributes, otf: Option<OpentypeSpec>| {
404 let family = self.family_by_key(family)?;
405 let attrs = attributes.into();
406 let font_id = family.data.match_(attrs, otf)?;
407 let data = self.base.fonts.get(font_id.to_usize())?;
408 Some(FontEntry {
409 index: &self.base,
410 family: family.data,
411 data,
412 })
413 };
414
415 self.families_by_spec(spec)
416 .iter()
417 .find_map(|family| query(*family, attrs, otf.clone()))
418 }
419
420 #[cfg(feature = "emacs")]
423 pub fn families_by_spec<'a>(
424 &'a self,
425 FontSpec {
426 family,
427 foundry,
428 registry,
429 name,
430 script,
431 lang,
432 otf,
433 ..
434 }: FontSpec,
435 ) -> Vec<FamilyId> {
436 if let Some(_) = foundry {
437 warn!("foundry is not yet supported");
438 }
439
440 if let Some(_) = name {
441 warn!("name is not yet supported");
442 }
443
444 let intersection = |families: Vec<FamilyId>, _families: Option<&Vec<FamilyId>>| {
445 if _families.is_none() {
446 return vec![];
447 }
448 let _families = _families.unwrap();
449
450 if families.is_empty() && !_families.is_empty() {
451 return _families.clone();
452 } else {
453 let unique_a = families.iter().collect::<HashSet<_>>();
454 let unique_b = _families.iter().collect::<HashSet<_>>();
455
456 return unique_a
457 .intersection(&unique_b)
458 .map(|id| **id)
459 .collect::<Vec<_>>();
460 }
461 };
462
463 let mut families = family
464 .map(|family| {
465 self.family_by_name(family.as_str())
466 .map(|entry| vec![entry.data.id])
467 })
468 .flatten()
469 .unwrap_or(vec![]);
470
471 if let Some((script, ..)) = otf {
472 let _families = self.families_by_script(script);
473 families = intersection(families, _families);
474 }
475
476 if let Some(regexp) = registry {
477 let _families = self.families_by_charset(regexp);
478 families = intersection(families, _families);
479 }
480
481 if let Some(script) = script {
482 let _families = self.families_by_emacs_script(script);
483 families = intersection(families, _families);
484 }
485
486 if let Some(lang) = lang {
487 let _f = lang
488 .to_639_1()
489 .map(|lang| Language::parse(lang))
490 .flatten()
491 .map(|lang| lang.to_opentype())
492 .flatten()
493 .map(|lang| self.families_by_lang(lang))
494 .flatten();
495 families = intersection(families, _f);
496 }
497
498 families.dedup();
499 families
500 }
501
502 #[cfg(feature = "emacs")]
503 pub fn families_by_charset(&self, regexp: String) -> Option<&Vec<FamilyId>> {
504 let string_match_p = |regexp: &str, string: &str, start: Option<i64>| {
505 let re = Regex::new(&lisp_regex_to_rust(regexp)).ok().unwrap();
506
507 let start = start.unwrap_or(0) as usize;
508 if let Some(_) = re.captures_iter(&string[start..]).next() {
509 true
510 } else {
511 false
512 }
513 };
514 self.emacs_charset_map
515 .iter()
516 .find_map(|(charset, families)| {
517 if string_match_p(regexp.as_str(), charset.as_str(), Some(0)) {
518 return Some(families);
519 }
520 None
521 })
522 }
523
524 #[cfg(feature = "emacs")]
525 pub fn families_by_emacs_script(&self, script: String) -> Option<&Vec<FamilyId>> {
526 self.emacs_script_map
527 .iter()
528 .find_map(|(script_, families)| {
529 if script.as_str() == script_.as_str() {
530 return Some(families);
531 }
532 None
533 })
534 }
535
536 pub fn families_by_script(&self, lang: Tag) -> Option<&Vec<FamilyId>> {
537 self.script_tag_map.iter().find_map(|(lang_, families)| {
538 if lang == *lang_ {
539 return Some(families);
540 }
541 None
542 })
543 }
544
545 pub fn families_by_lang(&self, lang: Tag) -> Option<&Vec<FamilyId>> {
546 self.language_tag_map.iter().find_map(|(lang_, families)| {
547 if lang == *lang_ {
548 return Some(families);
549 }
550 None
551 })
552 }
553
554 pub fn family_by_key<'a>(&'a self, key: impl Into<FamilyKey<'a>>) -> Option<FamilyEntry<'a>> {
556 match key.into() {
557 FamilyKey::Id(id) => self.family_by_id(id),
558 FamilyKey::Name(name) => self.family_by_name(name),
559 FamilyKey::Generic(generic) => {
560 self.family_by_id(self.generic.get(generic as usize).copied()??)
561 }
562 }
563 }
564
565 pub fn family_by_name<'a>(&'a self, name: &str) -> Option<FamilyEntry<'a>> {
567 let mut s = LowercaseString::new();
568 let name = s.get(name)?;
569 let id = if let Some(generic) = GenericFamily::parse(name) {
570 self.generic.get(generic as usize).copied()??
571 } else {
572 *self.base.family_map.get(name)?
573 };
574
575 self.family_by_id(id)
576 }
577
578 pub fn family_by_id<'a>(&'a self, id: FamilyId) -> Option<FamilyEntry<'a>> {
580 let data = self.families.get(id.to_usize())?;
581 Some(FamilyEntry {
582 index: &self.base,
583 data,
584 })
585 }
586
587 pub fn font_by_id<'a>(&'a self, id: FontId) -> Option<FontEntry<'a>> {
589 let data = self.base.fonts.get(id.to_usize())?;
590 let family = self.families.get(data.family.to_usize())?;
591 Some(FontEntry {
592 index: &self.base,
593 family,
594 data,
595 })
596 }
597}
598
599#[derive(Copy, Clone)]
601pub struct FamilyEntry<'a> {
602 index: &'a BaseIndex,
603 data: &'a FamilyData,
604}
605
606impl<'a> FamilyEntry<'a> {
607 pub fn id(&self) -> FamilyId {
609 self.data.id
610 }
611
612 pub fn name(&self) -> &str {
614 self.data.name.as_str()
615 }
616
617 pub fn fonts(&'a self) -> impl Iterator<Item = FontEntry<'a>> + 'a {
619 self.data.fonts.iter().filter_map(move |f| {
620 let data = self.index.fonts.get(f.id.to_usize())?;
621 Some(FontEntry {
622 index: self.index,
623 family: self.data,
624 data,
625 })
626 })
627 }
628}
629
630#[derive(Copy, Clone)]
632pub struct FontEntry<'a> {
633 index: &'a BaseIndex,
634 family: &'a FamilyData,
635 data: &'a FontData,
636}
637
638impl<'a> FontEntry<'a> {
639 pub fn id(&self) -> FontId {
641 self.data.id
642 }
643
644 pub fn source(&self) -> SourceEntry<'a> {
646 SourceEntry {
647 index: self.index,
648 data: &self.index.sources[self.data.source.to_usize()],
649 }
650 }
651
652 pub fn index(&self) -> u32 {
654 self.data.index
655 }
656
657 pub fn offset(&self) -> u32 {
659 self.data.offset
660 }
661
662 pub fn family(&self) -> FamilyEntry<'a> {
664 FamilyEntry {
665 index: self.index,
666 data: self.family,
667 }
668 }
669
670 pub fn family_name(&self) -> &str {
672 self.family.name.as_str()
673 }
674
675 pub fn attributes(&self) -> Attributes {
677 self.data.attributes
678 }
679
680 pub fn cache_key(&self) -> CacheKey {
681 self.data.key
682 }
683
684 pub fn selector(
685 &self,
686 attrs: RequestedAttributes,
687 ) -> (FontId, Attributes, RequestedAttributes) {
688 (self.data.id, self.data.attributes, attrs)
689 }
690}
691
692#[derive(Copy, Clone)]
694pub struct SourceEntry<'a> {
695 pub index: &'a BaseIndex,
696 data: &'a SourceData,
697}
698
699impl<'a> SourceEntry<'a> {
700 pub fn id(&self) -> SourceId {
702 self.data.id
703 }
704
705 pub fn path(&self) -> Option<&Path> {
707 match &self.data.kind {
708 SourceKind::Memory(..) => None,
709 SourceKind::File(data) => Some(&data.path),
710 }
711 }
712}
713
714#[cfg(feature = "emacs")]
717fn lisp_regex_to_rust(regexp: &str) -> String {
718 let mut norm_regex = String::new();
719 let mut chars = regexp.chars().peekable();
720 while let Some(ch) = chars.next() {
721 match ch {
722 '(' => norm_regex.push_str("\\("),
723 ')' => norm_regex.push_str("\\)"),
724 '\\' if matches!(chars.peek(), Some('(' | ')')) => {
725 norm_regex.push(chars.next().unwrap());
726 }
727 c => norm_regex.push(c),
728 }
729 }
730 norm_regex
731}