1use std::collections::HashMap;
2use std::sync::Arc;
3
4use skrifa::metrics::Metrics;
5use skrifa::prelude::Size;
6use skrifa::MetadataProvider;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct FontId(
11 pub u32,
13);
14
15pub type FontData = Arc<dyn AsRef<[u8]> + Send + Sync>;
20
21#[derive(Clone, Copy, Debug, PartialEq)]
23pub struct FontAttrs {
24 pub weight: u16,
26 pub italic: bool,
28 pub stretch: f32,
30}
31
32pub const NORMAL_STRETCH: f32 = 100.0;
34
35impl Default for FontAttrs {
36 fn default() -> Self {
37 Self {
38 weight: 400,
39 italic: false,
40 stretch: NORMAL_STRETCH,
41 }
42 }
43}
44
45struct SharedFace {
50 data: FontData,
51 face_index: u32,
54 charmap: HashMap<u32, u32>,
57 shaper_data: harfrust::ShaperData,
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
68pub struct FontUid(
69 pub u64,
71);
72
73impl FontUid {
74 fn next() -> FontUid {
75 use std::sync::atomic::{AtomicU64, Ordering};
76 static COUNTER: AtomicU64 = AtomicU64::new(1);
77 FontUid(COUNTER.fetch_add(1, Ordering::Relaxed))
78 }
79}
80
81pub struct Font {
86 uid: FontUid,
87 shared: Arc<SharedFace>,
88 variation_coordinates: Vec<([u8; 4], f32)>,
92 variation_location: skrifa::instance::Location,
95 shaper_instance: Option<harfrust::ShaperInstance>,
97 family: String,
98 aliases: Vec<String>,
102 attrs: FontAttrs,
103 units_per_em: f32,
104 ascent: f32,
106 descent: f32,
107 line_gap: f32,
108 bounds: Option<(f32, f32, f32, f32)>,
110 underline: Option<(f32, f32)>,
112 strikeout: Option<(f32, f32)>,
113}
114
115impl std::fmt::Debug for Font {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 f.debug_struct("Font")
118 .field("uid", &self.uid)
119 .field("family", &self.family)
120 .field("attrs", &self.attrs)
121 .finish_non_exhaustive()
122 }
123}
124
125impl Font {
126 fn parse(
127 family: &str,
128 attrs: FontAttrs,
129 data: FontData,
130 face_index: u32,
131 variation_coordinates: Vec<([u8; 4], f32)>,
132 ) -> Option<Self> {
133 let shared = SharedFace::parse(data, face_index)?;
134 Self::at_coordinates(shared, family, attrs, variation_coordinates)
135 }
136
137 fn at_coordinates(
140 shared: Arc<SharedFace>,
141 family: &str,
142 attrs: FontAttrs,
143 variation_coordinates: Vec<([u8; 4], f32)>,
144 ) -> Option<Self> {
145 let bytes: &[u8] = (*shared.data).as_ref();
146 let font = skrifa::FontRef::from_index(bytes, shared.face_index).ok()?;
147 let variation_location = font.axes().location(
148 variation_coordinates
149 .iter()
150 .map(|(tag, value)| (skrifa::Tag::new(tag), *value)),
151 );
152 let metrics = Metrics::new(&font, Size::unscaled(), &variation_location);
153 let shaper_instance = (!variation_coordinates.is_empty()).then(|| {
154 let harf = harfrust::FontRef::from_index(bytes, shared.face_index).ok();
155 harf.map(|harf| {
156 harfrust::ShaperInstance::from_variations(
157 &harf,
158 variation_coordinates
159 .iter()
160 .map(|(tag, value)| harfrust::Variation {
161 tag: harfrust::Tag::new(tag),
162 value: *value,
163 }),
164 )
165 })
166 });
167 Some(Self {
168 uid: FontUid::next(),
169 family: family.to_owned(),
170 aliases: Vec::new(),
171 attrs,
172 units_per_em: metrics.units_per_em as f32,
173 ascent: metrics.ascent,
174 descent: metrics.descent,
175 line_gap: metrics.leading,
176 bounds: metrics.bounds.map(|b| (b.x_min, b.y_min, b.x_max, b.y_max)),
177 underline: metrics.underline.map(|d| (d.offset, d.thickness)),
178 strikeout: metrics.strikeout.map(|d| (d.offset, d.thickness)),
179 shared,
180 variation_coordinates,
181 variation_location,
182 shaper_instance: shaper_instance.flatten(),
183 })
184 }
185
186 pub fn data(&self) -> &[u8] {
190 (*self.shared.data).as_ref()
191 }
192
193 pub fn face_index(&self) -> u32 {
195 self.shared.face_index
196 }
197
198 pub fn variation_coordinates(&self) -> &[([u8; 4], f32)] {
202 &self.variation_coordinates
203 }
204
205 pub(crate) fn variation_location(&self) -> &skrifa::instance::Location {
206 &self.variation_location
207 }
208
209 pub(crate) fn shaper_instance(&self) -> Option<&harfrust::ShaperInstance> {
210 self.shaper_instance.as_ref()
211 }
212
213 pub fn uid(&self) -> FontUid {
215 self.uid
216 }
217
218 pub fn family(&self) -> &str {
220 &self.family
221 }
222
223 pub fn aliases(&self) -> &[String] {
225 &self.aliases
226 }
227
228 pub fn matches(&self, name: &str) -> bool {
232 self.family.eq_ignore_ascii_case(name)
233 || self.aliases.iter().any(|a| a.eq_ignore_ascii_case(name))
234 }
235
236 pub fn add_alias(&mut self, name: &str) {
238 if !self.matches(name) {
239 self.aliases.push(name.to_owned());
240 }
241 }
242
243 pub fn attrs(&self) -> FontAttrs {
245 self.attrs
246 }
247
248 pub fn ascent_px(&self, size: f32) -> f32 {
250 self.ascent * size / self.units_per_em
251 }
252
253 pub fn descent_px(&self, size: f32) -> f32 {
255 -self.descent * size / self.units_per_em
256 }
257
258 pub fn line_height_px(&self, size: f32) -> f32 {
260 (self.ascent - self.descent + self.line_gap) * size / self.units_per_em
261 }
262
263 pub fn units_per_em(&self) -> f32 {
265 self.units_per_em
266 }
267
268 pub fn ink_box_px(&self, size: f32) -> Option<(f32, f32, f32, f32)> {
273 let k = size / self.units_per_em;
274 self.bounds
275 .map(|(x0, y0, x1, y1)| (x0 * k, y0 * k, x1 * k, y1 * k))
276 }
277
278 pub fn covers(&self, ch: char) -> bool {
280 self.shared.charmap.contains_key(&(ch as u32))
281 }
282
283 pub fn glyph_for(&self, ch: char) -> Option<u32> {
285 self.shared.charmap.get(&(ch as u32)).copied()
286 }
287
288 pub(crate) fn shaper_data(&self) -> &harfrust::ShaperData {
289 &self.shared.shaper_data
290 }
291
292 pub fn underline_px(&self, size: f32) -> (f32, f32) {
297 self.decoration_px(self.underline, size, -0.1, 0.05)
298 }
299
300 pub fn strikeout_px(&self, size: f32) -> (f32, f32) {
305 self.decoration_px(self.strikeout, size, 0.3, 0.05)
306 }
307
308 fn decoration_px(
309 &self,
310 metric: Option<(f32, f32)>,
311 size: f32,
312 default_offset: f32,
313 default_thickness: f32,
314 ) -> (f32, f32) {
315 match metric {
316 Some((offset, thickness)) => (
318 offset * size / self.units_per_em,
319 (thickness * size / self.units_per_em).max(0.5),
320 ),
321 None => (size * default_offset, (size * default_thickness).max(0.5)),
322 }
323 }
324}
325
326#[derive(Clone, Debug, Default, PartialEq)]
330pub struct FontDemand {
331 pub families: Vec<String>,
333 pub codepoints: Vec<(char, FontAttrs)>,
335}
336
337impl FontDemand {
338 pub fn is_empty(&self) -> bool {
340 self.families.is_empty() && self.codepoints.is_empty()
341 }
342
343 pub(crate) fn add_family(&mut self, name: &str) {
344 if !self.families.iter().any(|f| f == name) {
345 self.families.push(name.to_owned());
346 }
347 }
348
349 pub(crate) fn add_codepoint(&mut self, ch: char, attrs: FontAttrs) {
350 if !self.codepoints.contains(&(ch, attrs)) {
351 self.codepoints.push((ch, attrs));
352 }
353 }
354}
355
356pub trait FontSource {
362 fn family(&mut self, name: &str) -> Vec<Font>;
364
365 fn face_for_codepoint(&mut self, codepoint: char, attrs: FontAttrs) -> Option<Font>;
367}
368
369#[derive(Default, Clone)]
374pub struct FaceSet {
375 fonts: Vec<Arc<Font>>,
378 fallbacks: Vec<FontId>,
379}
380
381impl FaceSet {
382 pub fn new() -> Self {
384 Self::default()
385 }
386
387 pub fn register(&mut self, family: &str, bytes: Vec<u8>) -> Option<FontId> {
391 self.register_with(family, FontAttrs::default(), bytes)
392 }
393
394 pub fn register_with(
398 &mut self,
399 family: &str,
400 attrs: FontAttrs,
401 bytes: Vec<u8>,
402 ) -> Option<FontId> {
403 let data = unwrapped(Arc::new(bytes))?;
404 let font = Font::parse(family, attrs, data, 0, Vec::new())?;
405 Some(self.add(font))
406 }
407
408 pub fn add(&mut self, font: Font) -> FontId {
410 self.fonts.push(Arc::new(font));
411 FontId(self.fonts.len() as u32 - 1)
412 }
413
414 pub fn with_font(&self, font: Font) -> (FaceSet, FontId) {
418 let mut next = self.clone();
419 let id = next.add(font);
420 (next, id)
421 }
422
423 pub fn with_fallbacks(&self, fallbacks: Vec<FontId>) -> FaceSet {
427 let mut next = self.clone();
428 next.fallbacks = fallbacks;
429 next
430 }
431
432 pub fn add_fallback(&mut self, id: FontId) {
437 self.fallbacks.push(id);
438 }
439
440 pub fn grown_by(&self, source: &mut dyn FontSource, demand: &FontDemand) -> Option<FaceSet> {
446 let mut next = self.clone();
447 let mut grew = false;
448 for name in &demand.families {
449 if next.family(name).is_some() {
450 continue;
453 }
454 grew |= next.register_answers(source.family(name), name);
455 }
456 for &(codepoint, attrs) in &demand.codepoints {
457 grew |= next.register_fallback_answer(source, codepoint, attrs);
458 }
459 grew.then_some(next)
460 }
461
462 fn register_answers(&mut self, faces: Vec<Font>, requested_name: &str) -> bool {
463 let mut added = false;
464 for mut font in faces {
465 font.add_alias(requested_name);
466 self.add(font);
467 added = true;
468 }
469 added
470 }
471
472 fn register_fallback_answer(
473 &mut self,
474 source: &mut dyn FontSource,
475 codepoint: char,
476 attrs: FontAttrs,
477 ) -> bool {
478 if is_private_use(codepoint) {
479 return false;
483 }
484 if self.covers_anywhere(codepoint) {
485 return false;
488 }
489 let Some(font) = source.face_for_codepoint(codepoint, attrs) else {
490 return false;
491 };
492 let id = self.add(font);
493 self.add_fallback(id);
494 true
495 }
496
497 fn covers_anywhere(&self, codepoint: char) -> bool {
498 self.fonts.iter().any(|font| font.covers(codepoint))
499 }
500
501 pub fn is_empty(&self) -> bool {
503 self.fonts.is_empty()
504 }
505
506 pub fn len(&self) -> usize {
508 self.fonts.len()
509 }
510
511 pub fn get_arc(&self, id: FontId) -> Arc<Font> {
517 self.fonts[id.0 as usize].clone()
518 }
519
520 pub fn get(&self, id: FontId) -> &Font {
526 &self.fonts[id.0 as usize]
527 }
528
529 pub fn family(&self, name: &str) -> Option<FontId> {
531 let at = self.fonts.iter().position(|f| f.matches(name))?;
532 Some(FontId(at as u32))
533 }
534
535 pub fn faces<'a>(&'a self, name: &'a str) -> impl Iterator<Item = FontId> + 'a {
539 self.variants(name).map(|(id, _)| id)
540 }
541
542 pub fn family_variant(&self, name: &str, attrs: FontAttrs) -> Option<FontId> {
547 self.nearest(self.variants(name), attrs)
548 }
549
550 pub fn resolve(&self, families: &[String], attrs: FontAttrs, ch: char) -> FontId {
560 self.resolve_covered(families, attrs, ch).0
561 }
562
563 pub fn resolve_covered(
571 &self,
572 families: &[String],
573 attrs: FontAttrs,
574 ch: char,
575 ) -> (FontId, bool) {
576 for name in families {
577 let covering = self.variants(name).filter(|(_, f)| f.covers(ch));
578 if let Some(id) = self.nearest(covering, attrs) {
579 return (id, true);
580 }
581 }
582 let covering_fallbacks = self
583 .fallbacks
584 .iter()
585 .map(|id| (*id, self.get(*id)))
586 .filter(|(_, f)| f.covers(ch));
587 if let Some(id) = self.nearest(covering_fallbacks, attrs) {
588 return (id, true);
589 }
590 (self.tofu_face(families, attrs), false)
591 }
592
593 fn variants<'a>(&'a self, name: &'a str) -> impl Iterator<Item = (FontId, &'a Font)> {
595 self.fonts
596 .iter()
597 .enumerate()
598 .filter(move |(_, f)| f.matches(name))
599 .map(|(at, f)| (FontId(at as u32), f.as_ref()))
600 }
601
602 fn nearest<'a>(
607 &self,
608 faces: impl Iterator<Item = (FontId, &'a Font)>,
609 attrs: FontAttrs,
610 ) -> Option<FontId> {
611 faces
612 .min_by_key(|(_, f)| {
613 (
614 stretch_distance(f.attrs.stretch, attrs.stretch),
615 f.attrs.italic != attrs.italic,
616 f.attrs.weight.abs_diff(attrs.weight),
617 )
618 })
619 .map(|(id, _)| id)
620 }
621
622 fn tofu_face(&self, families: &[String], attrs: FontAttrs) -> FontId {
625 self.tofu_face_opt(families, attrs).unwrap_or_else(|| {
626 panic!(
627 "FontCollection has no fonts registered — register() one before building paragraphs"
628 );
629 })
630 }
631
632 pub(crate) fn tofu_face_opt(&self, families: &[String], attrs: FontAttrs) -> Option<FontId> {
637 families
638 .iter()
639 .find_map(|name| self.family_variant(name, attrs))
640 .or_else(|| self.fallbacks.first().copied())
641 .or_else(|| (!self.fonts.is_empty()).then_some(FontId(0)))
642 }
643
644 pub(crate) fn resolve_covered_opt(
647 &self,
648 families: &[String],
649 attrs: FontAttrs,
650 ch: char,
651 ) -> Option<(FontId, bool)> {
652 if self.fonts.is_empty() {
653 return None;
654 }
655 Some(self.resolve_covered(families, attrs, ch))
656 }
657}
658
659impl Font {
660 pub fn from_bytes(bytes: Vec<u8>) -> Option<Font> {
665 Self::from_data(Arc::new(bytes), 0)
666 }
667
668 pub fn from_data(data: FontData, face_index: u32) -> Option<Font> {
673 let data = unwrapped(data)?;
674 Self::instance(data, face_index, Vec::new())
675 }
676
677 pub fn instances_from_data(data: FontData, face_index: u32) -> Vec<Font> {
682 let Some(data) = unwrapped(data) else {
683 return Vec::new();
684 };
685 let instances = named_instance_coordinates((*data).as_ref(), face_index);
686 if instances.is_empty() {
687 return Self::instance(data, face_index, Vec::new())
688 .into_iter()
689 .collect();
690 }
691 let Some(shared) = SharedFace::parse(data, face_index) else {
694 return Vec::new();
695 };
696 instances
697 .into_iter()
698 .filter_map(|coordinates| Self::shared_instance(shared.clone(), coordinates))
699 .collect()
700 }
701
702 fn instance(data: FontData, face_index: u32, coordinates: Vec<([u8; 4], f32)>) -> Option<Font> {
705 let shared = SharedFace::parse(data, face_index)?;
706 Self::shared_instance(shared, coordinates)
707 }
708
709 fn shared_instance(shared: Arc<SharedFace>, coordinates: Vec<([u8; 4], f32)>) -> Option<Font> {
711 let bytes: &[u8] = (*shared.data).as_ref();
712 let (family, aliases) = embedded_names(bytes, shared.face_index)?;
713 let attrs = instance_attrs(embedded_attrs(bytes, shared.face_index), &coordinates);
714 let mut font = Font::at_coordinates(shared, &family, attrs, coordinates)?;
715 for name in &aliases {
716 font.add_alias(name);
717 }
718 Some(font)
719 }
720}
721
722impl SharedFace {
723 fn parse(data: FontData, face_index: u32) -> Option<Arc<Self>> {
724 let bytes: &[u8] = (*data).as_ref();
725 let font = skrifa::FontRef::from_index(bytes, face_index).ok()?;
726 let charmap = font
727 .charmap()
728 .mappings()
729 .map(|(code, glyph)| (code, glyph.to_u32()))
730 .collect();
731 let harf = harfrust::FontRef::from_index(bytes, face_index).ok()?;
732 let shaper_data = harfrust::ShaperData::new(&harf);
733 Some(Arc::new(Self {
734 data,
735 face_index,
736 charmap,
737 shaper_data,
738 }))
739 }
740}
741
742fn named_instance_coordinates(bytes: &[u8], face_index: u32) -> Vec<Vec<([u8; 4], f32)>> {
744 let Ok(font) = skrifa::FontRef::from_index(bytes, face_index) else {
745 return Vec::new();
746 };
747 let axis_tags: Vec<[u8; 4]> = font
748 .axes()
749 .iter()
750 .map(|axis| axis.tag().to_be_bytes())
751 .collect();
752 font.named_instances()
753 .iter()
754 .map(|instance| {
755 axis_tags
756 .iter()
757 .copied()
758 .zip(instance.user_coords())
759 .collect()
760 })
761 .collect()
762}
763
764fn instance_attrs(base: FontAttrs, coordinates: &[([u8; 4], f32)]) -> FontAttrs {
767 let mut attrs = base;
768 for (tag, value) in coordinates {
769 match tag {
770 b"wght" => attrs.weight = value.clamp(1.0, 1000.0) as u16,
771 b"ital" => attrs.italic = *value >= 0.5,
772 b"slnt" => attrs.italic = attrs.italic || *value < 0.0,
773 b"wdth" => attrs.stretch = value.clamp(1.0, 1000.0),
775 _ => {}
776 }
777 }
778 attrs
779}
780
781fn stretch_distance(candidate: f32, wanted: f32) -> u32 {
785 ((candidate - wanted).abs() * 16.0) as u32
786}
787
788#[cfg(feature = "woff2")]
792fn unwrapped(data: FontData) -> Option<FontData> {
793 let bytes: &[u8] = (*data).as_ref();
794 if !woff2_patched::decode::is_woff2(bytes) {
795 return Some(data);
796 }
797 let unpacked = woff2_patched::decode::convert_woff2_to_ttf(&mut &bytes[..]).ok()?;
798 Some(Arc::new(unpacked))
799}
800
801#[cfg(not(feature = "woff2"))]
802fn unwrapped(data: FontData) -> Option<FontData> {
803 Some(data)
804}
805
806fn embedded_names(data: &[u8], face_index: u32) -> Option<(String, Vec<String>)> {
810 use swash::StringId;
811 let font = swash::FontRef::from_index(data, face_index as usize)?;
812 let strings = font.localized_strings();
813 let pick = |id: StringId| {
814 strings
815 .find_by_id(id, Some("en"))
816 .or_else(|| strings.find_by_id(id, None))
817 .map(|s| s.to_string())
818 };
819 let primary = pick(StringId::TypographicFamily).or_else(|| pick(StringId::Family))?;
820 let aliases = strings
821 .filter(|s| matches!(s.id(), StringId::Family | StringId::TypographicFamily))
822 .map(|s| s.to_string())
823 .filter(|name| *name != primary)
824 .collect();
825 Some((primary, aliases))
826}
827
828fn embedded_attrs(data: &[u8], face_index: u32) -> FontAttrs {
830 let Some(font) = swash::FontRef::from_index(data, face_index as usize) else {
831 return FontAttrs::default();
832 };
833 let attrs = font.attributes();
834 FontAttrs {
835 weight: attrs.weight().0,
836 italic: attrs.style() != swash::Style::Normal,
837 stretch: attrs.stretch().to_percentage(),
838 }
839}
840
841fn is_private_use(codepoint: char) -> bool {
844 matches!(
845 codepoint,
846 '\u{E000}'..='\u{F8FF}' | '\u{F0000}'..='\u{FFFFD}' | '\u{100000}'..='\u{10FFFD}'
847 )
848}
849
850#[derive(Default)]
855pub struct FontCollection {
856 faces: FaceSet,
857 sources: Vec<Box<dyn FontSource>>,
860 unanswered: FontDemand,
862}
863
864impl FontCollection {
865 pub fn new() -> FontCollection {
867 FontCollection::default()
868 }
869
870 pub fn faces(&self) -> &FaceSet {
874 &self.faces
875 }
876
877 pub fn register(&mut self, family: &str, bytes: Vec<u8>) -> Option<FontId> {
881 self.faces.register(family, bytes)
882 }
883
884 pub fn add(&mut self, font: Font) -> FontId {
886 self.faces.add(font)
887 }
888
889 pub fn add_fallback(&mut self, id: FontId) {
893 self.faces.add_fallback(id);
894 }
895
896 pub fn get(&self, id: FontId) -> &Font {
902 self.faces.get(id)
903 }
904
905 pub fn len(&self) -> usize {
907 self.faces.len()
908 }
909
910 pub fn family(&self, name: &str) -> Option<FontId> {
912 self.faces.family(name)
913 }
914
915 pub fn add_source(&mut self, source: impl FontSource + 'static) {
917 self.sources.push(Box::new(source));
918 }
919
920 pub fn add_boxed_source(&mut self, source: Box<dyn FontSource>) {
922 self.sources.push(source);
923 }
924
925 pub fn is_empty(&self) -> bool {
927 self.faces.is_empty()
928 }
929
930 pub fn adopt_faces(&mut self, faces: FaceSet) {
934 self.faces = faces;
935 }
936
937 pub fn take_unanswered(&mut self) -> FontDemand {
942 std::mem::take(&mut self.unanswered)
943 }
944
945 pub(crate) fn require_family(&mut self, name: &str) -> bool {
949 if self.faces.family(name).is_some() {
950 return true;
951 }
952 for source in &mut self.sources {
953 let faces = source.family(name);
954 if self.faces.register_answers(faces, name) {
955 return true;
956 }
957 }
958 self.unanswered.add_family(name);
959 false
960 }
961
962 pub(crate) fn require_codepoint(&mut self, codepoint: char, attrs: FontAttrs) -> bool {
964 if self.faces.covers_anywhere(codepoint) {
965 return true;
966 }
967 for index in 0..self.sources.len() {
968 let (head, tail) = self.sources.split_at_mut(index);
969 let _ = head;
970 let source = &mut tail[0];
971 if self
972 .faces
973 .register_fallback_answer(source.as_mut(), codepoint, attrs)
974 {
975 return true;
976 }
977 }
978 self.unanswered.add_codepoint(codepoint, attrs);
979 false
980 }
981}