Skip to content

Commit 653d061

Browse files
committed
feat: working system fonts
1 parent b35d7bb commit 653d061

File tree

3 files changed

+123
-22
lines changed

3 files changed

+123
-22
lines changed

crates/bevy_text/src/font.rs

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,76 @@ use bevy_reflect::TypePath;
1616
///
1717
/// Bevy currently loads a single font face as a single `Font` asset.
1818
#[derive(Debug, TypePath, Clone, Asset)]
19-
pub struct Font {
19+
pub enum Font {
2020
/// Content of a font file as bytes
21-
pub data: Arc<Vec<u8>>,
21+
Data(Arc<Vec<u8>>),
22+
/// References a font installed on the system by family, weight, stretch, and style.
23+
System {
24+
/// A list of font families that satisfy this font requirement
25+
families: Vec<Family>,
26+
/// Specifies the weight of glyphs in the font, their degree of blackness or stroke thickness.
27+
///
28+
/// See [`cosmic_text::Weight`] for details.
29+
weight: cosmic_text::Weight,
30+
/// A face [width](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass).
31+
///
32+
/// See [`cosmic_text::Stretch`] for details.
33+
stretch: cosmic_text::Stretch,
34+
/// Allows italic or oblique faces to be selected.
35+
///
36+
/// See [`cosmic_text::Style`] for details.
37+
style: cosmic_text::Style,
38+
},
39+
}
40+
41+
/// A font family specifier, either by name or generic category.
42+
///
43+
/// See [`cosmic_text::Family`] for details.
44+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
45+
pub enum Family {
46+
/// The name of a font family of choice.
47+
///
48+
/// This must be a *Typographic Family* (ID 16) or a *Family Name* (ID 1) in terms of TrueType.
49+
/// Meaning you have to pass a family without any additional suffixes like _Bold_, _Italic_,
50+
/// _Regular_, etc.
51+
///
52+
/// Localized names are allowed.
53+
Name(String),
54+
55+
/// Serif fonts represent the formal text style for a script.
56+
Serif,
57+
58+
/// Glyphs in sans-serif fonts, as the term is used in CSS, are generally low contrast
59+
/// and have stroke endings that are plain — without any flaring, cross stroke,
60+
/// or other ornamentation.
61+
SansSerif,
62+
63+
/// Glyphs in cursive fonts generally use a more informal script style,
64+
/// and the result looks more like handwritten pen or brush writing than printed letterwork.
65+
Cursive,
66+
67+
/// Fantasy fonts are primarily decorative or expressive fonts that
68+
/// contain decorative or expressive representations of characters.
69+
Fantasy,
70+
71+
/// The sole criterion of a monospace font is that all glyphs have the same fixed width.
72+
MonoSpace,
73+
}
74+
75+
impl Family {
76+
/// References variants to create a [`cosmic_text::Family`].
77+
///
78+
/// This is required for querying the underlying [`cosmic_text::fontdb::Database`]
79+
pub fn as_fontdb_family(&self) -> cosmic_text::Family<'_> {
80+
match self {
81+
Family::Name(name) => cosmic_text::Family::Name(name),
82+
Family::Serif => cosmic_text::Family::Serif,
83+
Family::SansSerif => cosmic_text::Family::SansSerif,
84+
Family::Cursive => cosmic_text::Family::Cursive,
85+
Family::Fantasy => cosmic_text::Family::Fantasy,
86+
Family::MonoSpace => cosmic_text::Family::Monospace,
87+
}
88+
}
2289
}
2390

2491
impl Font {
@@ -28,8 +95,6 @@ impl Font {
2895
) -> Result<Self, cosmic_text::ttf_parser::FaceParsingError> {
2996
use cosmic_text::ttf_parser;
3097
ttf_parser::Face::parse(&font_data, 0)?;
31-
Ok(Self {
32-
data: Arc::new(font_data),
33-
})
98+
Ok(Self::Data(Arc::new(font_data)))
3499
}
35100
}

crates/bevy_text/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,16 @@ pub use pipeline::*;
5353
pub use text::*;
5454
pub use text_access::*;
5555

56+
pub use cosmic_text::{Stretch, Style, Weight};
57+
5658
/// The text prelude.
5759
///
5860
/// This includes the most common types in this crate, re-exported for your convenience.
5961
pub mod prelude {
6062
#[doc(hidden)]
6163
pub use crate::{
62-
Font, Justify, LineBreak, TextColor, TextError, TextFont, TextLayout, TextSpan,
64+
Family, Font, Justify, LineBreak, Stretch, Style, TextColor, TextError, TextFont,
65+
TextLayout, TextSpan, Weight,
6366
};
6467
}
6568

crates/bevy_text/src/pipeline.rs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bevy_math::{Rect, UVec2, Vec2};
1313
use bevy_platform::collections::HashMap;
1414
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
1515

16-
use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};
16+
use cosmic_text::{Attrs, Buffer, Metrics, Shaping, Wrap};
1717

1818
use crate::{
1919
error::TextError, ComputedTextBlock, Font, FontAtlasSets, FontSmoothing, Justify, LineBreak,
@@ -44,6 +44,7 @@ impl CosmicFontSystem {
4444
if load_system_fonts {
4545
db.load_system_fonts();
4646
}
47+
4748
Self(cosmic_text::FontSystem::new_with_locale_and_db(locale, db))
4849
}
4950
}
@@ -151,7 +152,8 @@ impl TextPipeline {
151152
font_system,
152153
&mut self.map_handle_to_font_id,
153154
fonts,
154-
);
155+
)
156+
.ok_or(TextError::NoSuchFont)?;
155157

156158
// Save spans that aren't zero-sized.
157159
if scale_factor <= 0.0 || text_font.font_size <= 0.0 {
@@ -507,34 +509,65 @@ pub fn load_font_to_fontdb(
507509
font_system: &mut cosmic_text::FontSystem,
508510
map_handle_to_font_id: &mut HashMap<AssetId<Font>, (cosmic_text::fontdb::ID, Arc<str>)>,
509511
fonts: &Assets<Font>,
510-
) -> FontFaceInfo {
512+
) -> Option<FontFaceInfo> {
511513
let font_handle = text_font.font.clone();
512-
let (face_id, family_name) = map_handle_to_font_id
513-
.entry(font_handle.id())
514-
.or_insert_with(|| {
514+
515+
let (face_id, family_name) = match map_handle_to_font_id.get_mut(&font_handle.id()) {
516+
Some((face_id, family_name)) => (face_id, family_name),
517+
None => {
515518
let font = fonts.get(font_handle.id()).expect(
516519
"Tried getting a font that was not available, probably due to not being loaded yet",
517520
);
518-
let data = Arc::clone(&font.data);
519-
let ids = font_system
520-
.db_mut()
521-
.load_font_source(cosmic_text::fontdb::Source::Binary(data));
522521

523-
// TODO: it is assumed this is the right font face
524-
let face_id = *ids.last().unwrap();
522+
let face_id = match font {
523+
Font::Data(data) => {
524+
let data = Arc::clone(data);
525+
526+
let ids = font_system
527+
.db_mut()
528+
.load_font_source(cosmic_text::fontdb::Source::Binary(data));
529+
530+
// TODO: it is assumed this is the right font face
531+
*ids.last().unwrap()
532+
}
533+
Font::System {
534+
families,
535+
weight,
536+
stretch,
537+
style,
538+
} => {
539+
let families = families
540+
.iter()
541+
.map(|family| family.as_fontdb_family())
542+
.collect::<Vec<_>>();
543+
let query = cosmic_text::fontdb::Query {
544+
families: &families,
545+
weight: *weight,
546+
stretch: *stretch,
547+
style: *style,
548+
};
549+
550+
font_system.db().query(&query)?
551+
}
552+
};
553+
525554
let face = font_system.db().face(face_id).unwrap();
526555
let family_name = Arc::from(face.families[0].0.as_str());
527556

557+
map_handle_to_font_id.insert(font_handle.id(), (face_id, family_name));
558+
let (face_id, family_name) = map_handle_to_font_id.get_mut(&font_handle.id()).unwrap();
528559
(face_id, family_name)
529-
});
560+
}
561+
};
562+
530563
let face = font_system.db().face(*face_id).unwrap();
531564

532-
FontFaceInfo {
565+
Some(FontFaceInfo {
533566
stretch: face.stretch,
534567
style: face.style,
535568
weight: face.weight,
536569
family_name: family_name.clone(),
537-
}
570+
})
538571
}
539572

540573
/// Translates [`TextFont`] to [`Attrs`].
@@ -547,7 +580,7 @@ fn get_attrs<'a>(
547580
) -> Attrs<'a> {
548581
Attrs::new()
549582
.metadata(span_index)
550-
.family(Family::Name(&face_info.family_name))
583+
.family(cosmic_text::Family::Name(&face_info.family_name))
551584
.stretch(face_info.stretch)
552585
.style(face_info.style)
553586
.weight(face_info.weight)

0 commit comments

Comments
 (0)