Skip to content

Commit 3815d53

Browse files
committed
Add mac_core library for FFI bindings to CoreFoundation/CoreText
1 parent 45b7066 commit 3815d53

14 files changed

Lines changed: 857 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ members = [
5353
"crates/errors",
5454
"crates/geturl",
5555
"crates/io_base",
56+
"crates/mac_core",
5657
"crates/pdf_io",
5758
"crates/status_base",
5859
"crates/xdv",

crates/mac_core/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "tectonic_mac_core"
3+
license = "MIT"
4+
version = "0.0.0-dev.0"
5+
edition = "2021"
6+
7+
[dependencies]
8+
libc = "0.2"
9+
10+
[build-dependencies]
11+
tectonic_cfg_support = { path = "../cfg_support", version = "0.0.0-dev.0" }
12+
13+
[package.metadata.internal_dep_versions]
14+
tectonic_cfg_support = "thiscommit:aeRoo7oa"

crates/mac_core/build.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use tectonic_cfg_support::target_cfg;
2+
3+
fn main() {
4+
let is_mac_os = target_cfg!(target_os = "macos");
5+
6+
if is_mac_os {
7+
println!("cargo:rustc-link-lib=framework=Foundation");
8+
println!("cargo:rustc-link-lib=framework=CoreFoundation");
9+
println!("cargo:rustc-link-lib=framework=CoreGraphics");
10+
println!("cargo:rustc-link-lib=framework=CoreText");
11+
println!("cargo:rustc-link-lib=framework=AppKit");
12+
}
13+
}

crates/mac_core/src/array.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use super::{sys, CoreType};
2+
use std::marker::PhantomData;
3+
use std::ops::Index;
4+
use std::ptr;
5+
use std::ptr::NonNull;
6+
7+
cfty! {
8+
CFArray<T> : CFArrayGetTypeID
9+
}
10+
11+
impl<T: CoreType> CFArray<T> {
12+
pub fn empty() -> CFArray<T> {
13+
let ptr = unsafe {
14+
sys::CFArrayCreate(
15+
ptr::null_mut(),
16+
ptr::null_mut(),
17+
0,
18+
&sys::kCFTypeArrayCallBacks,
19+
)
20+
};
21+
CFArray::new_owned(NonNull::new(ptr.cast_mut()).unwrap())
22+
}
23+
24+
pub fn new(values: &[T]) -> CFArray<T> {
25+
let ptr = unsafe {
26+
sys::CFArrayCreate(
27+
ptr::null_mut(),
28+
values.as_ptr().cast_mut().cast(),
29+
values.len() as sys::CFIndex,
30+
&sys::kCFTypeArrayCallBacks,
31+
)
32+
};
33+
CFArray::new_owned(NonNull::new(ptr.cast_mut()).unwrap())
34+
}
35+
36+
pub fn len(&self) -> usize {
37+
unsafe { sys::CFArrayGetCount(self.0.as_ptr()) as usize }
38+
}
39+
40+
pub fn is_empty(&self) -> bool {
41+
self.len() == 0
42+
}
43+
}
44+
45+
impl<T: CoreType> Index<usize> for CFArray<T> {
46+
type Output = T;
47+
48+
fn index(&self, index: usize) -> &Self::Output {
49+
let ptr =
50+
unsafe { sys::CFArrayGetValueAtIndex(self.0.cast().as_ptr(), index as sys::CFIndex) }
51+
.cast::<T>();
52+
if ptr.is_null() {
53+
panic!("Index {index} out of bounds for CFArray");
54+
}
55+
unsafe { &*ptr }
56+
}
57+
}

crates/mac_core/src/dict.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use super::{sys, CoreType};
2+
use std::marker::PhantomData;
3+
use std::mem::MaybeUninit;
4+
use std::ptr::NonNull;
5+
use std::{mem, ptr};
6+
7+
pub trait AsPtr {
8+
fn as_ptr(&self) -> *const ();
9+
#[allow(clippy::len_without_is_empty)]
10+
fn len(&self) -> usize;
11+
}
12+
13+
impl<T> AsPtr for Vec<T> {
14+
fn as_ptr(&self) -> *const () {
15+
self.as_ptr().cast()
16+
}
17+
18+
fn len(&self) -> usize {
19+
self.len()
20+
}
21+
}
22+
23+
impl<T, const N: usize> AsPtr for [T; N] {
24+
fn as_ptr(&self) -> *const () {
25+
<[T]>::as_ptr(self).cast()
26+
}
27+
28+
fn len(&self) -> usize {
29+
N
30+
}
31+
}
32+
33+
pub trait Pairs {
34+
type Keys: AsPtr;
35+
type Values: AsPtr;
36+
37+
fn into_pairs(self) -> (Self::Keys, Self::Values);
38+
}
39+
40+
impl<K, V, const N: usize> Pairs for [(K, V); N] {
41+
type Keys = [K; N];
42+
type Values = [V; N];
43+
44+
fn into_pairs(self) -> (Self::Keys, Self::Values) {
45+
let mut keys: [MaybeUninit<K>; N] = unsafe { MaybeUninit::uninit().assume_init() };
46+
let mut values: [MaybeUninit<V>; N] = unsafe { MaybeUninit::uninit().assume_init() };
47+
48+
for (idx, (key, val)) in self.into_iter().enumerate() {
49+
keys[idx].write(key);
50+
values[idx].write(val);
51+
}
52+
53+
unsafe {
54+
(
55+
mem::transmute_copy::<_, [K; N]>(&keys),
56+
mem::transmute_copy::<_, [V; N]>(&values),
57+
)
58+
}
59+
}
60+
}
61+
62+
impl<K, V> Pairs for Vec<(K, V)> {
63+
type Keys = Vec<K>;
64+
type Values = Vec<V>;
65+
66+
fn into_pairs(self) -> (Self::Keys, Self::Values) {
67+
self.into_iter().unzip()
68+
}
69+
}
70+
71+
cfty! {
72+
CFDictionary<K, V> : CFDictionaryGetTypeID
73+
}
74+
75+
impl<K: CoreType, V: CoreType> CFDictionary<K, V> {
76+
pub fn new<P: Pairs>(pairs: P) -> CFDictionary<K, V> {
77+
let (keys, values) = pairs.into_pairs();
78+
79+
let ptr = unsafe {
80+
sys::CFDictionaryCreate(
81+
ptr::null_mut(),
82+
keys.as_ptr().cast_mut().cast(),
83+
values.as_ptr().cast_mut().cast(),
84+
keys.len() as sys::CFIndex,
85+
&sys::kCFTypeDictionaryKeyCallBacks,
86+
&sys::kCFTypeDictionaryValueCallBacks,
87+
)
88+
};
89+
CFDictionary::new_owned(NonNull::new(ptr.cast_mut()).unwrap())
90+
}
91+
}

crates/mac_core/src/font.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use super::{sys, CFString, CFType, CTFontDescriptor, CoreType};
2+
use std::ptr;
3+
use std::ptr::NonNull;
4+
5+
#[derive(Copy, Clone)]
6+
pub enum FontAttribute {
7+
Name,
8+
FamilyName,
9+
DisplayName,
10+
URL,
11+
CascadeList,
12+
}
13+
14+
impl FontAttribute {
15+
pub fn to_str(self) -> CFString {
16+
CFString::new_borrowed(NonNull::new(self.to_raw().cast_mut()).unwrap())
17+
}
18+
19+
pub fn to_raw(self) -> sys::CFStringRef {
20+
match self {
21+
FontAttribute::Name => unsafe { sys::kCTFontNameAttribute },
22+
FontAttribute::FamilyName => unsafe { sys::kCTFontFamilyNameAttribute },
23+
FontAttribute::DisplayName => unsafe { sys::kCTFontDisplayNameAttribute },
24+
FontAttribute::URL => unsafe { sys::kCTFontURLAttribute },
25+
FontAttribute::CascadeList => unsafe { sys::kCTFontCascadeListAttribute },
26+
}
27+
}
28+
}
29+
30+
#[derive(Copy, Clone)]
31+
pub enum FontNameKey {
32+
Full,
33+
Family,
34+
Style,
35+
PostScript,
36+
}
37+
38+
impl FontNameKey {
39+
pub fn to_str(self) -> CFString {
40+
CFString::new_borrowed(NonNull::new(self.to_raw().cast_mut()).unwrap())
41+
}
42+
43+
fn to_raw(self) -> sys::CFStringRef {
44+
match self {
45+
FontNameKey::Full => unsafe { sys::kCTFontFullNameKey },
46+
FontNameKey::Family => unsafe { sys::kCTFontFamilyNameKey },
47+
FontNameKey::Style => unsafe { sys::kCTFontStyleNameKey },
48+
FontNameKey::PostScript => unsafe { sys::kCTFontPostScriptNameKey },
49+
}
50+
}
51+
}
52+
53+
cfty! {
54+
CTFont : CTFontGetTypeID
55+
}
56+
57+
impl CTFont {
58+
pub fn new_descriptor(descriptor: &CTFontDescriptor, size: f64) -> CTFont {
59+
let ptr = unsafe {
60+
sys::CTFontCreateWithFontDescriptor(
61+
descriptor.as_type_ref(),
62+
size as sys::CGFloat,
63+
ptr::null_mut(),
64+
)
65+
};
66+
CTFont::new_owned(NonNull::new(ptr.cast_mut()).unwrap())
67+
}
68+
69+
pub fn attr(&self, attr: FontAttribute) -> Option<CFType> {
70+
let ptr = unsafe { sys::CTFontCopyAttribute(self.as_type_ref(), attr.to_raw()) };
71+
NonNull::new(ptr.cast_mut()).map(CFType::new_owned)
72+
}
73+
74+
pub fn name(&self, name: FontNameKey) -> Option<CFString> {
75+
let ptr = unsafe { sys::CTFontCopyName(self.as_type_ref(), name.to_raw()) };
76+
NonNull::new(ptr.cast_mut()).map(CFString::new_owned)
77+
}
78+
79+
pub fn localized_name(&self, name: FontNameKey) -> Option<CFString> {
80+
let ptr = unsafe {
81+
sys::CTFontCopyLocalizedName(self.as_type_ref(), name.to_raw(), ptr::null_mut())
82+
};
83+
NonNull::new(ptr.cast_mut()).map(CFString::new_owned)
84+
}
85+
}

crates/mac_core/src/font_desc.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use super::{sys, CFArray, CFDictionary, CFSet, CFString, CFType, CoreType, FontAttribute};
2+
use std::ptr::NonNull;
3+
4+
cfty! {
5+
CTFontDescriptor : CTFontDescriptorGetTypeID
6+
}
7+
8+
impl CTFontDescriptor {
9+
pub fn new_with_attrs(attrs: &CFDictionary<CFString, CFType>) -> CTFontDescriptor {
10+
let ptr = unsafe { sys::CTFontDescriptorCreateWithAttributes(attrs.as_type_ref()) };
11+
CTFontDescriptor::new_owned(NonNull::new(ptr.cast_mut()).unwrap())
12+
}
13+
14+
pub fn copy_with_attrs(&self, attrs: &CFDictionary<CFString, CFType>) -> CTFontDescriptor {
15+
let ptr = unsafe {
16+
sys::CTFontDescriptorCreateCopyWithAttributes(self.as_type_ref(), attrs.as_type_ref())
17+
};
18+
CTFontDescriptor::new_owned(NonNull::new(ptr.cast_mut()).unwrap())
19+
}
20+
21+
pub fn matching_font_descriptors(
22+
&self,
23+
mandatory: &CFSet<CFString>,
24+
) -> CFArray<CTFontDescriptor> {
25+
let ptr = unsafe {
26+
sys::CTFontDescriptorCreateMatchingFontDescriptors(
27+
self.as_type_ref(),
28+
mandatory.as_type_ref(),
29+
)
30+
};
31+
NonNull::new(ptr.cast_mut())
32+
.map(CFArray::new_owned)
33+
.unwrap_or_else(CFArray::empty)
34+
}
35+
36+
pub fn attr(&self, attr: FontAttribute) -> Option<CFType> {
37+
let ptr = unsafe { sys::CTFontDescriptorCopyAttribute(self.as_type_ref(), attr.to_raw()) };
38+
NonNull::new(ptr.cast_mut()).map(CFType::new_owned)
39+
}
40+
}

0 commit comments

Comments
 (0)