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(pub u32);
11
12pub type FontData = Arc<dyn AsRef<[u8]> + Send + Sync>;
15
16#[derive(Clone, Copy, Debug, PartialEq)]
21pub struct FontAttrs {
22 pub weight: u16,
24 pub italic: bool,
25 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)]
67pub struct FontUid(pub u64);
68
69impl FontUid {
70 fn next() -> FontUid {
71 use std::sync::atomic::{AtomicU64, Ordering};
72 static COUNTER: AtomicU64 = AtomicU64::new(1);
73 FontUid(COUNTER.fetch_add(1, Ordering::Relaxed))
74 }
75}
76
77pub struct Font {
78 uid: FontUid,
79 shared: Arc<SharedFace>,
80 variation_coordinates: Vec<([u8; 4], f32)>,
84 variation_location: skrifa::instance::Location,
87 shaper_instance: Option<harfrust::ShaperInstance>,
89 family: String,
90 aliases: Vec<String>,
94 attrs: FontAttrs,
95 units_per_em: f32,
96 ascent: f32,
98 descent: f32,
99 line_gap: f32,
100 bounds: Option<(f32, f32, f32, f32)>,
102 underline: Option<(f32, f32)>,
104 strikeout: Option<(f32, f32)>,
105}
106
107impl std::fmt::Debug for Font {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 f.debug_struct("Font")
110 .field("uid", &self.uid)
111 .field("family", &self.family)
112 .field("attrs", &self.attrs)
113 .finish_non_exhaustive()
114 }
115}
116
117impl Font {
118 fn parse(
119 family: &str,
120 attrs: FontAttrs,
121 data: FontData,
122 face_index: u32,
123 variation_coordinates: Vec<([u8; 4], f32)>,
124 ) -> Option<Self> {
125 let shared = SharedFace::parse(data, face_index)?;
126 Self::at_coordinates(shared, family, attrs, variation_coordinates)
127 }
128
129 fn at_coordinates(
132 shared: Arc<SharedFace>,
133 family: &str,
134 attrs: FontAttrs,
135 variation_coordinates: Vec<([u8; 4], f32)>,
136 ) -> Option<Self> {
137 let bytes: &[u8] = (*shared.data).as_ref();
138 let font = skrifa::FontRef::from_index(bytes, shared.face_index).ok()?;
139 let variation_location = font.axes().location(
140 variation_coordinates
141 .iter()
142 .map(|(tag, value)| (skrifa::Tag::new(tag), *value)),
143 );
144 let metrics = Metrics::new(&font, Size::unscaled(), &variation_location);
145 let shaper_instance = (!variation_coordinates.is_empty()).then(|| {
146 let harf = harfrust::FontRef::from_index(bytes, shared.face_index).ok();
147 harf.map(|harf| {
148 harfrust::ShaperInstance::from_variations(
149 &harf,
150 variation_coordinates
151 .iter()
152 .map(|(tag, value)| harfrust::Variation {
153 tag: harfrust::Tag::new(tag),
154 value: *value,
155 }),
156 )
157 })
158 });
159 Some(Self {
160 uid: FontUid::next(),
161 family: family.to_owned(),
162 aliases: Vec::new(),
163 attrs,
164 units_per_em: metrics.units_per_em as f32,
165 ascent: metrics.ascent,
166 descent: metrics.descent,
167 line_gap: metrics.leading,
168 bounds: metrics.bounds.map(|b| (b.x_min, b.y_min, b.x_max, b.y_max)),
169 underline: metrics.underline.map(|d| (d.offset, d.thickness)),
170 strikeout: metrics.strikeout.map(|d| (d.offset, d.thickness)),
171 shared,
172 variation_coordinates,
173 variation_location,
174 shaper_instance: shaper_instance.flatten(),
175 })
176 }
177
178 pub fn data(&self) -> &[u8] {
181 (*self.shared.data).as_ref()
182 }
183
184 pub fn face_index(&self) -> u32 {
186 self.shared.face_index
187 }
188
189 pub fn variation_coordinates(&self) -> &[([u8; 4], f32)] {
192 &self.variation_coordinates
193 }
194
195 pub(crate) fn variation_location(&self) -> &skrifa::instance::Location {
196 &self.variation_location
197 }
198
199 pub(crate) fn shaper_instance(&self) -> Option<&harfrust::ShaperInstance> {
200 self.shaper_instance.as_ref()
201 }
202
203 pub fn uid(&self) -> FontUid {
205 self.uid
206 }
207
208 pub fn family(&self) -> &str {
209 &self.family
210 }
211
212 pub fn aliases(&self) -> &[String] {
213 &self.aliases
214 }
215
216 pub fn matches(&self, name: &str) -> bool {
219 self.family.eq_ignore_ascii_case(name)
220 || self.aliases.iter().any(|a| a.eq_ignore_ascii_case(name))
221 }
222
223 pub fn add_alias(&mut self, name: &str) {
225 if !self.matches(name) {
226 self.aliases.push(name.to_owned());
227 }
228 }
229
230 pub fn attrs(&self) -> FontAttrs {
231 self.attrs
232 }
233
234 pub fn ascent_px(&self, size: f32) -> f32 {
236 self.ascent * size / self.units_per_em
237 }
238
239 pub fn descent_px(&self, size: f32) -> f32 {
241 -self.descent * size / self.units_per_em
242 }
243
244 pub fn line_height_px(&self, size: f32) -> f32 {
246 (self.ascent - self.descent + self.line_gap) * size / self.units_per_em
247 }
248
249 pub fn units_per_em(&self) -> f32 {
250 self.units_per_em
251 }
252
253 pub fn ink_box_px(&self, size: f32) -> Option<(f32, f32, f32, f32)> {
257 let k = size / self.units_per_em;
258 self.bounds
259 .map(|(x0, y0, x1, y1)| (x0 * k, y0 * k, x1 * k, y1 * k))
260 }
261
262 pub fn covers(&self, ch: char) -> bool {
263 self.shared.charmap.contains_key(&(ch as u32))
264 }
265
266 pub fn glyph_for(&self, ch: char) -> Option<u32> {
267 self.shared.charmap.get(&(ch as u32)).copied()
268 }
269
270 pub(crate) fn shaper_data(&self) -> &harfrust::ShaperData {
271 &self.shared.shaper_data
272 }
273
274 pub fn underline_px(&self, size: f32) -> (f32, f32) {
277 self.decoration_px(self.underline, size, -0.1, 0.05)
278 }
279
280 pub fn strikeout_px(&self, size: f32) -> (f32, f32) {
282 self.decoration_px(self.strikeout, size, 0.3, 0.05)
283 }
284
285 fn decoration_px(
286 &self,
287 metric: Option<(f32, f32)>,
288 size: f32,
289 default_offset: f32,
290 default_thickness: f32,
291 ) -> (f32, f32) {
292 match metric {
293 Some((offset, thickness)) => (
295 offset * size / self.units_per_em,
296 (thickness * size / self.units_per_em).max(0.5),
297 ),
298 None => (size * default_offset, (size * default_thickness).max(0.5)),
299 }
300 }
301}
302
303#[derive(Clone, Debug, Default, PartialEq)]
312pub struct FontDemand {
313 pub families: Vec<String>,
314 pub codepoints: Vec<(char, FontAttrs)>,
315}
316
317impl FontDemand {
318 pub fn is_empty(&self) -> bool {
319 self.families.is_empty() && self.codepoints.is_empty()
320 }
321
322 pub(crate) fn add_family(&mut self, name: &str) {
323 if !self.families.iter().any(|f| f == name) {
324 self.families.push(name.to_owned());
325 }
326 }
327
328 pub(crate) fn add_codepoint(&mut self, ch: char, attrs: FontAttrs) {
329 if !self.codepoints.contains(&(ch, attrs)) {
330 self.codepoints.push((ch, attrs));
331 }
332 }
333}
334
335pub trait FontSource {
341 fn family(&mut self, name: &str) -> Vec<Font>;
344
345 fn face_for_codepoint(&mut self, codepoint: char, attrs: FontAttrs) -> Option<Font>;
347}
348
349#[derive(Default, Clone)]
353pub struct FaceSet {
354 fonts: Vec<Arc<Font>>,
357 fallbacks: Vec<FontId>,
358}
359
360impl FaceSet {
361 pub fn new() -> Self {
362 Self::default()
363 }
364
365 pub fn register(&mut self, family: &str, bytes: Vec<u8>) -> Option<FontId> {
368 self.register_with(family, FontAttrs::default(), bytes)
369 }
370
371 pub fn register_with(
375 &mut self,
376 family: &str,
377 attrs: FontAttrs,
378 bytes: Vec<u8>,
379 ) -> Option<FontId> {
380 let data = unwrapped(Arc::new(bytes))?;
381 let font = Font::parse(family, attrs, data, 0, Vec::new())?;
382 Some(self.add(font))
383 }
384
385 pub fn add(&mut self, font: Font) -> FontId {
388 self.fonts.push(Arc::new(font));
389 FontId(self.fonts.len() as u32 - 1)
390 }
391
392 pub fn with_font(&self, font: Font) -> (FaceSet, FontId) {
396 let mut next = self.clone();
397 let id = next.add(font);
398 (next, id)
399 }
400
401 pub fn with_fallbacks(&self, fallbacks: Vec<FontId>) -> FaceSet {
403 let mut next = self.clone();
404 next.fallbacks = fallbacks;
405 next
406 }
407
408 pub fn add_fallback(&mut self, id: FontId) {
412 self.fallbacks.push(id);
413 }
414
415 pub fn grown_by(&self, source: &mut dyn FontSource, demand: &FontDemand) -> Option<FaceSet> {
427 let mut next = self.clone();
428 let mut grew = false;
429 for name in &demand.families {
430 if next.family(name).is_some() {
431 continue;
434 }
435 grew |= next.register_answers(source.family(name), name);
436 }
437 for &(codepoint, attrs) in &demand.codepoints {
438 grew |= next.register_fallback_answer(source, codepoint, attrs);
439 }
440 grew.then_some(next)
441 }
442
443 fn register_answers(&mut self, faces: Vec<Font>, requested_name: &str) -> bool {
444 let mut added = false;
445 for mut font in faces {
446 font.add_alias(requested_name);
447 self.add(font);
448 added = true;
449 }
450 added
451 }
452
453 fn register_fallback_answer(
454 &mut self,
455 source: &mut dyn FontSource,
456 codepoint: char,
457 attrs: FontAttrs,
458 ) -> bool {
459 if is_private_use(codepoint) {
460 return false;
464 }
465 if self.covers_anywhere(codepoint) {
466 return false;
469 }
470 let Some(font) = source.face_for_codepoint(codepoint, attrs) else {
471 return false;
472 };
473 let id = self.add(font);
474 self.add_fallback(id);
475 true
476 }
477
478 fn covers_anywhere(&self, codepoint: char) -> bool {
479 self.fonts.iter().any(|font| font.covers(codepoint))
480 }
481
482 pub fn is_empty(&self) -> bool {
485 self.fonts.is_empty()
486 }
487
488 pub fn len(&self) -> usize {
491 self.fonts.len()
492 }
493
494 pub fn get_arc(&self, id: FontId) -> Arc<Font> {
497 self.fonts[id.0 as usize].clone()
498 }
499
500 pub fn get(&self, id: FontId) -> &Font {
501 &self.fonts[id.0 as usize]
502 }
503
504 pub fn family(&self, name: &str) -> Option<FontId> {
505 let at = self.fonts.iter().position(|f| f.matches(name))?;
506 Some(FontId(at as u32))
507 }
508
509 pub fn faces<'a>(&'a self, name: &'a str) -> impl Iterator<Item = FontId> + 'a {
515 self.variants(name).map(|(id, _)| id)
516 }
517
518 pub fn family_variant(&self, name: &str, attrs: FontAttrs) -> Option<FontId> {
521 self.nearest(self.variants(name), attrs)
522 }
523
524 pub fn resolve(&self, families: &[String], attrs: FontAttrs, ch: char) -> FontId {
530 self.resolve_covered(families, attrs, ch).0
531 }
532
533 pub fn resolve_covered(
538 &self,
539 families: &[String],
540 attrs: FontAttrs,
541 ch: char,
542 ) -> (FontId, bool) {
543 for name in families {
544 let covering = self.variants(name).filter(|(_, f)| f.covers(ch));
545 if let Some(id) = self.nearest(covering, attrs) {
546 return (id, true);
547 }
548 }
549 let covering_fallbacks = self
550 .fallbacks
551 .iter()
552 .map(|id| (*id, self.get(*id)))
553 .filter(|(_, f)| f.covers(ch));
554 if let Some(id) = self.nearest(covering_fallbacks, attrs) {
555 return (id, true);
556 }
557 (self.tofu_face(families, attrs), false)
558 }
559
560 fn variants<'a>(&'a self, name: &'a str) -> impl Iterator<Item = (FontId, &'a Font)> {
562 self.fonts
563 .iter()
564 .enumerate()
565 .filter(move |(_, f)| f.matches(name))
566 .map(|(at, f)| (FontId(at as u32), f.as_ref()))
567 }
568
569 fn nearest<'a>(
574 &self,
575 faces: impl Iterator<Item = (FontId, &'a Font)>,
576 attrs: FontAttrs,
577 ) -> Option<FontId> {
578 faces
579 .min_by_key(|(_, f)| {
580 (
581 stretch_distance(f.attrs.stretch, attrs.stretch),
582 f.attrs.italic != attrs.italic,
583 f.attrs.weight.abs_diff(attrs.weight),
584 )
585 })
586 .map(|(id, _)| id)
587 }
588
589 fn tofu_face(&self, families: &[String], attrs: FontAttrs) -> FontId {
592 self.tofu_face_opt(families, attrs).unwrap_or_else(|| {
593 panic!(
594 "FontCollection has no fonts registered — register() one before building paragraphs"
595 );
596 })
597 }
598
599 pub(crate) fn tofu_face_opt(&self, families: &[String], attrs: FontAttrs) -> Option<FontId> {
604 families
605 .iter()
606 .find_map(|name| self.family_variant(name, attrs))
607 .or_else(|| self.fallbacks.first().copied())
608 .or_else(|| (!self.fonts.is_empty()).then_some(FontId(0)))
609 }
610
611 pub(crate) fn resolve_covered_opt(
614 &self,
615 families: &[String],
616 attrs: FontAttrs,
617 ch: char,
618 ) -> Option<(FontId, bool)> {
619 if self.fonts.is_empty() {
620 return None;
621 }
622 Some(self.resolve_covered(families, attrs, ch))
623 }
624}
625
626impl Font {
627 pub fn from_bytes(bytes: Vec<u8>) -> Option<Font> {
633 Self::from_data(Arc::new(bytes), 0)
634 }
635
636 pub fn from_data(data: FontData, face_index: u32) -> Option<Font> {
640 let data = unwrapped(data)?;
641 Self::instance(data, face_index, Vec::new())
642 }
643
644 pub fn instances_from_data(data: FontData, face_index: u32) -> Vec<Font> {
649 let Some(data) = unwrapped(data) else {
650 return Vec::new();
651 };
652 let instances = named_instance_coordinates((*data).as_ref(), face_index);
653 if instances.is_empty() {
654 return Self::instance(data, face_index, Vec::new())
655 .into_iter()
656 .collect();
657 }
658 let Some(shared) = SharedFace::parse(data, face_index) else {
661 return Vec::new();
662 };
663 instances
664 .into_iter()
665 .filter_map(|coordinates| Self::shared_instance(shared.clone(), coordinates))
666 .collect()
667 }
668
669 fn instance(data: FontData, face_index: u32, coordinates: Vec<([u8; 4], f32)>) -> Option<Font> {
672 let shared = SharedFace::parse(data, face_index)?;
673 Self::shared_instance(shared, coordinates)
674 }
675
676 fn shared_instance(shared: Arc<SharedFace>, coordinates: Vec<([u8; 4], f32)>) -> Option<Font> {
678 let bytes: &[u8] = (*shared.data).as_ref();
679 let (family, aliases) = embedded_names(bytes, shared.face_index)?;
680 let attrs = instance_attrs(embedded_attrs(bytes, shared.face_index), &coordinates);
681 let mut font = Font::at_coordinates(shared, &family, attrs, coordinates)?;
682 for name in &aliases {
683 font.add_alias(name);
684 }
685 Some(font)
686 }
687}
688
689impl SharedFace {
690 fn parse(data: FontData, face_index: u32) -> Option<Arc<Self>> {
691 let bytes: &[u8] = (*data).as_ref();
692 let font = skrifa::FontRef::from_index(bytes, face_index).ok()?;
693 let charmap = font
694 .charmap()
695 .mappings()
696 .map(|(code, glyph)| (code, glyph.to_u32()))
697 .collect();
698 let harf = harfrust::FontRef::from_index(bytes, face_index).ok()?;
699 let shaper_data = harfrust::ShaperData::new(&harf);
700 Some(Arc::new(Self {
701 data,
702 face_index,
703 charmap,
704 shaper_data,
705 }))
706 }
707}
708
709fn named_instance_coordinates(bytes: &[u8], face_index: u32) -> Vec<Vec<([u8; 4], f32)>> {
711 let Ok(font) = skrifa::FontRef::from_index(bytes, face_index) else {
712 return Vec::new();
713 };
714 let axis_tags: Vec<[u8; 4]> = font
715 .axes()
716 .iter()
717 .map(|axis| axis.tag().to_be_bytes())
718 .collect();
719 font.named_instances()
720 .iter()
721 .map(|instance| {
722 axis_tags
723 .iter()
724 .copied()
725 .zip(instance.user_coords())
726 .collect()
727 })
728 .collect()
729}
730
731fn instance_attrs(base: FontAttrs, coordinates: &[([u8; 4], f32)]) -> FontAttrs {
734 let mut attrs = base;
735 for (tag, value) in coordinates {
736 match tag {
737 b"wght" => attrs.weight = value.clamp(1.0, 1000.0) as u16,
738 b"ital" => attrs.italic = *value >= 0.5,
739 b"slnt" => attrs.italic = attrs.italic || *value < 0.0,
740 b"wdth" => attrs.stretch = value.clamp(1.0, 1000.0),
742 _ => {}
743 }
744 }
745 attrs
746}
747
748fn stretch_distance(candidate: f32, wanted: f32) -> u32 {
752 ((candidate - wanted).abs() * 16.0) as u32
753}
754
755#[cfg(feature = "woff2")]
759fn unwrapped(data: FontData) -> Option<FontData> {
760 let bytes: &[u8] = (*data).as_ref();
761 if !woff2_patched::decode::is_woff2(bytes) {
762 return Some(data);
763 }
764 let unpacked = woff2_patched::decode::convert_woff2_to_ttf(&mut &bytes[..]).ok()?;
765 Some(Arc::new(unpacked))
766}
767
768#[cfg(not(feature = "woff2"))]
769fn unwrapped(data: FontData) -> Option<FontData> {
770 Some(data)
771}
772
773fn embedded_names(data: &[u8], face_index: u32) -> Option<(String, Vec<String>)> {
777 use swash::StringId;
778 let font = swash::FontRef::from_index(data, face_index as usize)?;
779 let strings = font.localized_strings();
780 let pick = |id: StringId| {
781 strings
782 .find_by_id(id, Some("en"))
783 .or_else(|| strings.find_by_id(id, None))
784 .map(|s| s.to_string())
785 };
786 let primary = pick(StringId::TypographicFamily).or_else(|| pick(StringId::Family))?;
787 let aliases = strings
788 .filter(|s| matches!(s.id(), StringId::Family | StringId::TypographicFamily))
789 .map(|s| s.to_string())
790 .filter(|name| *name != primary)
791 .collect();
792 Some((primary, aliases))
793}
794
795fn embedded_attrs(data: &[u8], face_index: u32) -> FontAttrs {
797 let Some(font) = swash::FontRef::from_index(data, face_index as usize) else {
798 return FontAttrs::default();
799 };
800 let attrs = font.attributes();
801 FontAttrs {
802 weight: attrs.weight().0,
803 italic: attrs.style() != swash::Style::Normal,
804 stretch: attrs.stretch().to_percentage(),
805 }
806}
807
808fn is_private_use(codepoint: char) -> bool {
811 matches!(
812 codepoint,
813 '\u{E000}'..='\u{F8FF}' | '\u{F0000}'..='\u{FFFFD}' | '\u{100000}'..='\u{10FFFD}'
814 )
815}
816
817#[derive(Default)]
824pub struct FontCollection {
825 faces: FaceSet,
826 sources: Vec<Box<dyn FontSource>>,
829 unanswered: FontDemand,
831}
832
833impl FontCollection {
834 pub fn new() -> FontCollection {
835 FontCollection::default()
836 }
837
838 pub fn faces(&self) -> &FaceSet {
840 &self.faces
841 }
842
843 pub fn register(&mut self, family: &str, bytes: Vec<u8>) -> Option<FontId> {
846 self.faces.register(family, bytes)
847 }
848
849 pub fn add(&mut self, font: Font) -> FontId {
850 self.faces.add(font)
851 }
852
853 pub fn add_fallback(&mut self, id: FontId) {
854 self.faces.add_fallback(id);
855 }
856
857 pub fn get(&self, id: FontId) -> &Font {
858 self.faces.get(id)
859 }
860
861 pub fn len(&self) -> usize {
862 self.faces.len()
863 }
864
865 pub fn family(&self, name: &str) -> Option<FontId> {
866 self.faces.family(name)
867 }
868
869 pub fn add_source(&mut self, source: impl FontSource + 'static) {
872 self.sources.push(Box::new(source));
873 }
874
875 pub fn add_boxed_source(&mut self, source: Box<dyn FontSource>) {
878 self.sources.push(source);
879 }
880
881 pub fn is_empty(&self) -> bool {
882 self.faces.is_empty()
883 }
884
885 pub fn adopt_faces(&mut self, faces: FaceSet) {
888 self.faces = faces;
889 }
890
891 pub fn take_unanswered(&mut self) -> FontDemand {
895 std::mem::take(&mut self.unanswered)
896 }
897
898 pub(crate) fn require_family(&mut self, name: &str) -> bool {
902 if self.faces.family(name).is_some() {
903 return true;
904 }
905 for source in &mut self.sources {
906 let faces = source.family(name);
907 if self.faces.register_answers(faces, name) {
908 return true;
909 }
910 }
911 self.unanswered.add_family(name);
912 false
913 }
914
915 pub(crate) fn require_codepoint(&mut self, codepoint: char, attrs: FontAttrs) -> bool {
917 if self.faces.covers_anywhere(codepoint) {
918 return true;
919 }
920 for index in 0..self.sources.len() {
921 let (head, tail) = self.sources.split_at_mut(index);
922 let _ = head;
923 let source = &mut tail[0];
924 if self
925 .faces
926 .register_fallback_answer(source.as_mut(), codepoint, attrs)
927 {
928 return true;
929 }
930 }
931 self.unanswered.add_codepoint(codepoint, attrs);
932 false
933 }
934}