Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 52b4233

Browse files
committedJan 24, 2025·
fontique: Update to objc2 0.6 and new CoreFoundation bindings
The `objc2` family of crates now provides bindings for CoreFoundation and CoreText (and much more), so we can use these rather than the old `core-foundation` and `core-text` crates. This also simplifies some of the code. One simplification assumes that `CTFontCreateUIFontForLanguage` won't fail.
1 parent 0424d18 commit 52b4233

File tree

3 files changed

+119
-159
lines changed

3 files changed

+119
-159
lines changed
 

‎Cargo.lock

+77-52
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎fontique/Cargo.toml

+4-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ unicode_script = ["dep:unicode-script"]
2525
system = [
2626
"std",
2727
"dep:windows", "dep:windows-core",
28-
"dep:core-text", "dep:core-foundation", "dep:objc2", "dep:objc2-foundation",
28+
"dep:objc2-core-text", "dep:objc2-core-foundation", "dep:objc2-foundation",
2929
"dep:fontconfig-cache-parser", "dep:roxmltree",
3030
]
3131

@@ -46,10 +46,9 @@ windows = { version = "0.58.0", features = ["implement", "Win32_Graphics_DirectW
4646
windows-core = { version = "0.58", optional = true }
4747

4848
[target.'cfg(target_vendor = "apple")'.dependencies]
49-
core-text = { version = "20.1.0", optional = true }
50-
core-foundation = { version = "0.9.4", optional = true }
51-
objc2 = { version = "0.5.2", optional = true }
52-
objc2-foundation = { version = "0.2.2", features = ["NSArray", "NSEnumerator", "NSPathUtilities", "NSString"], optional = true }
49+
objc2-foundation = { version = "0.3.0", optional = true, default-features = false, features = ["alloc", "NSArray", "NSEnumerator", "NSPathUtilities", "NSString"] }
50+
objc2-core-foundation = { version = "0.3.0", optional = true, default-features = false, features = ["CFBase"] }
51+
objc2-core-text = { version = "0.3.0", optional = true, default-features = false, features = ["CTFont", "CTFontDescriptor"] }
5352

5453
[target.'cfg(target_os = "linux")'.dependencies]
5554
fontconfig-cache-parser = { version = "0.2.0", optional = true }

‎fontique/src/backend/coretext.rs

+38-102
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,17 @@ use super::{
55
scan, FallbackKey, FamilyId, FamilyInfo, FamilyNameMap, GenericFamily, GenericFamilyMap,
66
};
77
use alloc::sync::Arc;
8+
use core::ptr::{null, null_mut};
89
use hashbrown::HashMap;
9-
use objc2::runtime::Bool;
10+
use objc2_core_foundation::{CFDictionaryCreate, CFRange, CFRetained, CFString, CFStringGetLength};
11+
use objc2_core_text::{
12+
CTFont, CTFontCopyFamilyName, CTFontCreateForString, CTFontCreateForStringWithLanguage,
13+
CTFontCreateUIFontForLanguage, CTFontCreateWithFontDescriptor,
14+
CTFontDescriptorCreateWithAttributes, CTFontUIFontType,
15+
};
1016
use objc2_foundation::{
1117
NSSearchPathDirectory, NSSearchPathDomainMask, NSSearchPathForDirectoriesInDomains,
1218
};
13-
use {
14-
core_foundation::{
15-
base::{CFRange, TCFType},
16-
dictionary::CFDictionary,
17-
string::{CFString, CFStringRef},
18-
},
19-
core_text::{
20-
font::{self, kCTFontSystemFontType, CTFont, CTFontRef, CTFontUIFontType},
21-
font_descriptor,
22-
},
23-
};
2419

2520
const DEFAULT_GENERIC_FAMILIES: &[(GenericFamily, &[&str])] = &[
2621
(GenericFamily::Serif, &["Times", "Times New Roman"]),
@@ -43,12 +38,11 @@ impl SystemFonts {
4338
pub(crate) fn new() -> Self {
4439
let paths = unsafe {
4540
NSSearchPathForDirectoriesInDomains(
46-
NSSearchPathDirectory::NSLibraryDirectory,
47-
NSSearchPathDomainMask::NSAllDomainsMask,
48-
Bool::YES,
41+
NSSearchPathDirectory::LibraryDirectory,
42+
NSSearchPathDomainMask::AllDomainsMask,
43+
true,
4944
)
50-
.as_ref()
51-
.iter()
45+
.into_iter()
5246
.map(|p| format!("{p}/Fonts/"))
5347
};
5448
let scanned = scan::ScannedCollection::from_paths(paths, 8);
@@ -77,97 +71,39 @@ impl SystemFonts {
7771
pub(crate) fn fallback(&mut self, key: impl Into<FallbackKey>) -> Option<FamilyId> {
7872
let key = key.into();
7973
let sample = key.script().sample()?;
80-
self.fallback_for_text(sample, key.locale(), false)
74+
let font = self.fallback_font_for_text(sample, key.locale(), false)?;
75+
let family_name = unsafe { CTFontCopyFamilyName(&font) };
76+
self.name_map.get(&family_name.to_string()).map(|n| n.id())
8177
}
82-
}
8378

84-
impl SystemFonts {
85-
fn fallback_for_text(
79+
fn fallback_font_for_text(
8680
&mut self,
8781
text: &str,
8882
locale: Option<&str>,
8983
prefer_ui: bool,
90-
) -> Option<FamilyId> {
91-
let font = fallback_for_text(text, locale, prefer_ui)?;
92-
self.name_map.get(&font.family_name()).map(|n| n.id())
93-
}
94-
}
95-
96-
fn fallback_for_text(text: &str, locale: Option<&str>, prefer_ui: bool) -> Option<CTFont> {
97-
let cf_text = CFString::new(text);
98-
let cf_text_range = CFRange::init(0, cf_text.char_len());
99-
let cf_locale = locale.map(CFString::new);
100-
let base_font = {
101-
let desc_attrs = CFDictionary::from_CFType_pairs(&[]);
102-
let mut desc = font_descriptor::new_from_attributes(&desc_attrs);
103-
if prefer_ui {
104-
if let Some(ui_desc) = ui_font_for_language(kCTFontSystemFontType, 0.0, None)
105-
.map(|font| font.copy_descriptor())
106-
{
107-
desc = ui_desc;
108-
}
109-
}
110-
font::new_from_descriptor(&desc, 0.0)
111-
};
112-
let font = unsafe {
113-
CTFont::wrap_under_create_rule(if let Some(locale) = cf_locale {
114-
CTFontCreateForStringWithLanguage(
115-
base_font.as_concrete_TypeRef(),
116-
cf_text.as_concrete_TypeRef(),
117-
cf_text_range,
118-
locale.as_concrete_TypeRef(),
119-
)
120-
} else {
121-
CTFontCreateForString(
122-
base_font.as_concrete_TypeRef(),
123-
cf_text.as_concrete_TypeRef(),
124-
cf_text_range,
125-
)
126-
})
127-
};
128-
Some(font)
129-
}
130-
131-
fn ui_font_for_language(
132-
ui_type: CTFontUIFontType,
133-
size: f64,
134-
language: Option<CFString>,
135-
) -> Option<CTFont> {
136-
unsafe {
137-
let font_ref = CTFontCreateUIFontForLanguage(
138-
ui_type,
139-
size,
140-
language
141-
.as_ref()
142-
.map(|x| x.as_concrete_TypeRef())
143-
.unwrap_or(std::ptr::null()),
144-
);
145-
if font_ref.is_null() {
146-
None
84+
) -> Option<CFRetained<CTFont>> {
85+
let text = CFString::from_str(text);
86+
let text_range = CFRange {
87+
location: 0,
88+
length: unsafe { CFStringGetLength(&text) },
89+
};
90+
let locale = locale.map(CFString::from_str);
91+
let base_font = if prefer_ui {
92+
unsafe { CTFontCreateUIFontForLanguage(CTFontUIFontType::System, 0.0, None).unwrap() }
14793
} else {
148-
Some(CTFont::wrap_under_create_rule(font_ref))
149-
}
94+
unsafe {
95+
let attrs = CFDictionaryCreate(None, null_mut(), null_mut(), 0, null(), null());
96+
let desc = CTFontDescriptorCreateWithAttributes(&attrs.unwrap());
97+
CTFontCreateWithFontDescriptor(&desc, 0.0, null())
98+
}
99+
};
100+
let font = unsafe {
101+
if let Some(locale) = locale {
102+
CTFontCreateForStringWithLanguage(&base_font, &text, text_range, Some(&locale))
103+
} else {
104+
CTFontCreateForString(&base_font, &text, text_range)
105+
}
106+
};
107+
Some(font)
150108
}
151109
}
152-
153-
#[link(name = "CoreText", kind = "framework")]
154-
extern "C" {
155-
fn CTFontCreateUIFontForLanguage(
156-
uiType: CTFontUIFontType,
157-
size: f64,
158-
language: CFStringRef,
159-
) -> CTFontRef;
160-
161-
fn CTFontCreateForString(
162-
currentFont: CTFontRef,
163-
string: CFStringRef,
164-
range: CFRange,
165-
) -> CTFontRef;
166-
167-
fn CTFontCreateForStringWithLanguage(
168-
currentFont: CTFontRef,
169-
string: CFStringRef,
170-
range: CFRange,
171-
language: CFStringRef,
172-
) -> CTFontRef;
173-
}

0 commit comments

Comments
 (0)
Please sign in to comment.