Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ The sections should follow the order `Added`, `Changed`, `Fixed`, and `Removed`.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

## 0.8.1

### Changed

- MSRV changed to 1.71.0
- Fixed leak after searching a font with fontconfig backend

## 0.8.0

### Changed
Expand Down
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "crossfont"
version = "0.8.0"
version = "0.8.1"
description = "Cross platform native font loading and rasterization"
authors = ["Christian Duerr <[email protected]>", "Joe Wilm <[email protected]>"]
repository = "https://github.com/alacritty/crossfont.git"
Expand All @@ -10,7 +10,7 @@ readme = "README.md"
categories = ["gui", "os"]
keywords = ["font"]
edition = "2021"
rust-version = "1.65.0"
rust-version = "1.71.0"

[dependencies]
libc = "0.2"
Expand All @@ -25,13 +25,17 @@ freetype-rs = "0.36.0"
pkg-config = "0.3"

[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25.0"
core-foundation = "0.9.3"
core-text = "20.1.0"
core-graphics = "0.23.1"
core-foundation-sys = "0.8.4"
objc = "0.2.7"
once_cell = "1.12"
objc2 = "0.5.1"
objc2-foundation = { version = "0.2.2", features = [
"NSString",
"NSUserDefaults",
"NSValue",
] }

[target.'cfg(windows)'.dependencies]
dwrote = { version = "0.11" }
Expand Down
44 changes: 26 additions & 18 deletions src/darwin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
//! Font rendering based on CoreText.

use std::collections::HashMap;
use std::ffi::c_char;
use std::ffi::CStr;
use std::iter;
use std::path::PathBuf;
use std::ptr;

use cocoa::base::{id, nil};
use cocoa::foundation::{NSInteger, NSString, NSUserDefaults};

use core_foundation::array::{CFArray, CFIndex};
use core_foundation::base::{CFType, ItemRef, TCFType};
use core_foundation::number::{CFNumber, CFNumberRef};
Expand All @@ -28,10 +24,10 @@ use core_text::font_descriptor::{
self, kCTFontColorGlyphsTrait, kCTFontDefaultOrientation, kCTFontEnabledAttribute,
CTFontDescriptor, SymbolicTraitAccessors,
};
use objc2::rc::{autoreleasepool, Retained};
use objc2_foundation::{ns_string, NSNumber, NSObject, NSObjectProtocol, NSString, NSUserDefaults};

use log::{trace, warn};
use objc::rc::autoreleasepool;
use objc::{class, msg_send, sel, sel_impl};
use once_cell::sync::Lazy;

pub mod byte_order;
Expand Down Expand Up @@ -274,30 +270,42 @@ fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
// other integer, or a missing value (the default), or a value of any other type, as leaving it
// enabled.
static FONT_SMOOTHING_ENABLED: Lazy<bool> = Lazy::new(|| {
autoreleasepool(|| unsafe {
let key = NSString::alloc(nil).init_str("AppleFontSmoothing");
let value: id = msg_send![id::standardUserDefaults(), objectForKey: key];
autoreleasepool(|_| {
let value = unsafe {
NSUserDefaults::standardUserDefaults().objectForKey(ns_string!("AppleFontSmoothing"))
};

if msg_send![value, isKindOfClass: class!(NSNumber)] {
let num_type: *const c_char = msg_send![value, objCType];
if num_type.is_null() {
return true;
}
let value = match value {
Some(value) => value,
None => return true,
};

// SAFETY: The values in `NSUserDefaults` are always subclasses of
// `NSObject`.
let value: Retained<NSObject> = unsafe { Retained::cast(value) };

if value.is_kind_of::<NSNumber>() {
// SAFETY: Just checked that the value is a NSNumber
let value: Retained<NSNumber> = unsafe { Retained::cast(value) };

// NSNumber's objCType method returns one of these strings depending on the size:
// q = quad (long long), l = long, i = int, s = short.
// This is done to reject booleans, which are NSNumbers with an objCType of "c", but
// macOS does not treat them the same as an integer 0 or 1 for this setting,
// it just ignores it.
let int_specifiers: [&[u8]; 4] = [b"q", b"l", b"i", b"s"];
if !int_specifiers.contains(&CStr::from_ptr(num_type).to_bytes()) {

let encoding = unsafe { CStr::from_ptr(value.objCType().as_ptr()).to_bytes() };
if !int_specifiers.contains(&encoding) {
return true;
}

let smoothing: NSInteger = msg_send![value, integerValue];
let smoothing = value.integerValue();
smoothing != 0
} else if msg_send![value, isKindOfClass: class!(NSString)] {
let smoothing: NSInteger = msg_send![value, integerValue];
} else if value.is_kind_of::<NSString>() {
// SAFETY: Just checked that the value is a NSString
let value: Retained<NSString> = unsafe { Retained::cast(value) };
let smoothing = unsafe { value.integerValue() };
smoothing != 0
} else {
true
Expand Down
4 changes: 2 additions & 2 deletions src/directwrite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ impl DirectWriteRasterizer {

Ok(RasterizedGlyph {
character,
width: (bounds.right - bounds.left) as i32,
height: (bounds.bottom - bounds.top) as i32,
width: bounds.right - bounds.left,
height: bounds.bottom - bounds.top,
top: -bounds.top,
left: bounds.left,
advance: (0, 0),
Expand Down
2 changes: 1 addition & 1 deletion src/ft/fc/char_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl CharSetRef {
}

pub fn count(&self) -> u32 {
unsafe { FcCharSetCount(self.as_ptr()) as u32 }
unsafe { FcCharSetCount(self.as_ptr()) }
}

pub fn union(&self, other: &CharSetRef) -> CharSet {
Expand Down
24 changes: 9 additions & 15 deletions src/ft/fc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ pub use pattern::{FtFaceLocation, Pattern, PatternHash, PatternRef};
/// The returned pattern is the result of Pattern::render_prepare.
pub fn font_match(config: &ConfigRef, pattern: &PatternRef) -> Option<Pattern> {
unsafe {
// What is this result actually used for? Seems redundant with
// return type.
let mut result = FcResultNoMatch;
let ptr = FcFontMatch(config.as_ptr(), pattern.as_ptr(), &mut result);

Expand All @@ -57,25 +55,21 @@ pub fn update_config() {

/// List fonts by closeness to the pattern.
pub fn font_sort(config: &ConfigRef, pattern: &PatternRef) -> Option<FontSet> {
unsafe {
// What is this result actually used for? Seems redundant with
// return type.
let ptr = unsafe {
let mut result = FcResultNoMatch;

let mut charsets: *mut _ = ptr::null_mut();
let ptr = FcFontSort(
FcFontSort(
config.as_ptr(),
pattern.as_ptr(),
1, // Trim font list.
&mut charsets,
ptr::null_mut(),
&mut result,
);
)
};

if ptr.is_null() {
None
} else {
Some(FontSet::from_ptr(ptr))
}
if ptr.is_null() {
None
} else {
Some(unsafe { FontSet::from_ptr(ptr) })
}
}

Expand Down
7 changes: 1 addition & 6 deletions src/ft/fc/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::ffi::{CStr, CString};
use std::fmt;
use std::mem;
use std::path::PathBuf;
use std::ptr::{self, NonNull};
use std::str;
Expand Down Expand Up @@ -45,11 +44,7 @@ impl<'a> StringPropertyIter<'a> {
};

if result == FcResultMatch {
// Transmute here is to extend lifetime of the str to that of the iterator.
//
// Potential unsafety? What happens if the pattern is modified while this ptr is
// borrowed out?
unsafe { mem::transmute(CStr::from_ptr(value as *const c_char).to_str().ok()?) }
unsafe { CStr::from_ptr(value as *const c_char).to_str().ok() }
} else {
None
}
Expand Down
5 changes: 2 additions & 3 deletions src/ft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,7 @@ impl FreeTypeRasterizer {
}
},
None => {
if !font_pattern.get_charset().map_or(false, |cs| cs.has_char(glyph.character))
{
if !font_pattern.get_charset().is_some_and(|cs| cs.has_char(glyph.character)) {
continue;
}

Expand Down Expand Up @@ -693,7 +692,7 @@ impl FreeTypeLoader {
font_key: FontKey,
) -> Result<Option<FontKey>, Error> {
if let Some(ft_face_location) = pattern.ft_face_location(0) {
if self.faces.get(&font_key).is_some() {
if self.faces.contains_key(&font_key) {
return Ok(Some(font_key));
}

Expand Down