From 9181ac9872082127b5fab62cfe8ee10cbea85ecb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 17 Mar 2023 12:01:27 +0100 Subject: [PATCH] Honor the `calt` font feature --- crates/gpui/src/platform/mac/fonts.rs | 57 ++++++++++++- .../gpui/src/platform/mac/fonts/open_type.rs | 83 +++++++++++++++++++ 2 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 crates/gpui/src/platform/mac/fonts/open_type.rs diff --git a/crates/gpui/src/platform/mac/fonts.rs b/crates/gpui/src/platform/mac/fonts.rs index 98ed541e35..b6fe297629 100644 --- a/crates/gpui/src/platform/mac/fonts.rs +++ b/crates/gpui/src/platform/mac/fonts.rs @@ -1,3 +1,5 @@ +mod open_type; + use crate::{ fonts::{Features, FontId, GlyphId, Metrics, Properties}, geometry::{ @@ -14,19 +16,29 @@ use core_foundation::{ array::CFIndex, attributed_string::{CFAttributedStringRef, CFMutableAttributedString}, base::{CFRange, TCFType}, + number::CFNumber, string::CFString, }; use core_graphics::{ base::{kCGImageAlphaPremultipliedLast, CGGlyph}, color_space::CGColorSpace, context::CGContext, + geometry::CGAffineTransform, +}; +use core_text::{ + font::{CTFont, CTFontRef}, + font_descriptor::{ + CTFontDescriptor, CTFontDescriptorCreateCopyWithFeature, CTFontDescriptorRef, + }, + line::CTLine, + string_attributes::kCTFontAttributeName, }; -use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName}; use font_kit::{ - handle::Handle, hinting::HintingOptions, source::SystemSource, sources::mem::MemSource, + font::Font, handle::Handle, hinting::HintingOptions, source::SystemSource, + sources::mem::MemSource, }; use parking_lot::RwLock; -use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc}; +use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, ptr, sync::Arc}; #[allow(non_upper_case_globals)] const kCGImageAlphaOnly: u32 = 7; @@ -134,7 +146,17 @@ impl FontSystemState { .select_family_by_name(name) .or_else(|_| self.system_source.select_family_by_name(name))?; for font in family.fonts() { - let font = font.load()?; + let mut font = font.load()?; + + if let Some(calt) = features.calt { + let value = if calt { + open_type::kContextualAlternatesOnSelector + } else { + open_type::kContextualAlternatesOffSelector + }; + font = toggle_open_type_feature(&font, open_type::kContextualAlternatesType, value); + } + let font_id = FontId(self.fonts.len()); font_ids.push(font_id); let postscript_name = font.postscript_name().unwrap(); @@ -490,6 +512,33 @@ extern "C" { start_index: CFIndex, width: f64, ) -> CFIndex; + + fn CTFontCreateCopyWithAttributes( + font: CTFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + attributes: CTFontDescriptorRef, + ) -> CTFontRef; +} + +fn toggle_open_type_feature(font: &Font, type_identifier: i32, selector_identifier: i32) -> Font { + let native_font = font.native_font(); + unsafe { + let new_descriptor = CTFontDescriptorCreateCopyWithFeature( + native_font.copy_descriptor().as_concrete_TypeRef(), + CFNumber::from(type_identifier).as_concrete_TypeRef(), + CFNumber::from(selector_identifier).as_concrete_TypeRef(), + ); + let new_descriptor = CTFontDescriptor::wrap_under_create_rule(new_descriptor); + let new_font = CTFontCreateCopyWithAttributes( + font.native_font().as_concrete_TypeRef(), + 0.0, + ptr::null(), + new_descriptor.as_concrete_TypeRef(), + ); + let new_font = CTFont::wrap_under_create_rule(new_font); + Font::from_native_font(new_font) + } } #[cfg(test)] diff --git a/crates/gpui/src/platform/mac/fonts/open_type.rs b/crates/gpui/src/platform/mac/fonts/open_type.rs new file mode 100644 index 0000000000..b2e17e6d6d --- /dev/null +++ b/crates/gpui/src/platform/mac/fonts/open_type.rs @@ -0,0 +1,83 @@ +#![allow(unused, non_upper_case_globals)] + +pub const kAltHalfWidthTextSelector: i32 = 6; +pub const kAltProportionalTextSelector: i32 = 5; +pub const kAlternateHorizKanaOffSelector: i32 = 1; +pub const kAlternateHorizKanaOnSelector: i32 = 0; +pub const kAlternateKanaType: i32 = 34; +pub const kAlternateVertKanaOffSelector: i32 = 3; +pub const kAlternateVertKanaOnSelector: i32 = 2; +pub const kCaseSensitiveLayoutOffSelector: i32 = 1; +pub const kCaseSensitiveLayoutOnSelector: i32 = 0; +pub const kCaseSensitiveLayoutType: i32 = 33; +pub const kCaseSensitiveSpacingOffSelector: i32 = 3; +pub const kCaseSensitiveSpacingOnSelector: i32 = 2; +pub const kContextualAlternatesOffSelector: i32 = 1; +pub const kContextualAlternatesOnSelector: i32 = 0; +pub const kContextualAlternatesType: i32 = 36; +pub const kContextualLigaturesOffSelector: i32 = 19; +pub const kContextualLigaturesOnSelector: i32 = 18; +pub const kContextualSwashAlternatesOffSelector: i32 = 5; +pub const kContextualSwashAlternatesOnSelector: i32 = 4; +pub const kDefaultLowerCaseSelector: i32 = 0; +pub const kDefaultUpperCaseSelector: i32 = 0; +pub const kHistoricalLigaturesOffSelector: i32 = 21; +pub const kHistoricalLigaturesOnSelector: i32 = 20; +pub const kHojoCharactersSelector: i32 = 12; +pub const kJIS2004CharactersSelector: i32 = 11; +pub const kLowerCasePetiteCapsSelector: i32 = 2; +pub const kLowerCaseSmallCapsSelector: i32 = 1; +pub const kLowerCaseType: i32 = 37; +pub const kMathematicalGreekOffSelector: i32 = 11; +pub const kMathematicalGreekOnSelector: i32 = 10; +pub const kNLCCharactersSelector: i32 = 13; +pub const kQuarterWidthTextSelector: i32 = 4; +pub const kScientificInferiorsSelector: i32 = 4; +pub const kStylisticAltEightOffSelector: i32 = 17; +pub const kStylisticAltEightOnSelector: i32 = 16; +pub const kStylisticAltEighteenOffSelector: i32 = 37; +pub const kStylisticAltEighteenOnSelector: i32 = 36; +pub const kStylisticAltElevenOffSelector: i32 = 23; +pub const kStylisticAltElevenOnSelector: i32 = 22; +pub const kStylisticAltFifteenOffSelector: i32 = 31; +pub const kStylisticAltFifteenOnSelector: i32 = 30; +pub const kStylisticAltFiveOffSelector: i32 = 11; +pub const kStylisticAltFiveOnSelector: i32 = 10; +pub const kStylisticAltFourOffSelector: i32 = 9; +pub const kStylisticAltFourOnSelector: i32 = 8; +pub const kStylisticAltFourteenOffSelector: i32 = 29; +pub const kStylisticAltFourteenOnSelector: i32 = 28; +pub const kStylisticAltNineOffSelector: i32 = 19; +pub const kStylisticAltNineOnSelector: i32 = 18; +pub const kStylisticAltNineteenOffSelector: i32 = 39; +pub const kStylisticAltNineteenOnSelector: i32 = 38; +pub const kStylisticAltOneOffSelector: i32 = 3; +pub const kStylisticAltOneOnSelector: i32 = 2; +pub const kStylisticAltSevenOffSelector: i32 = 15; +pub const kStylisticAltSevenOnSelector: i32 = 14; +pub const kStylisticAltSeventeenOffSelector: i32 = 35; +pub const kStylisticAltSeventeenOnSelector: i32 = 34; +pub const kStylisticAltSixOffSelector: i32 = 13; +pub const kStylisticAltSixOnSelector: i32 = 12; +pub const kStylisticAltSixteenOffSelector: i32 = 33; +pub const kStylisticAltSixteenOnSelector: i32 = 32; +pub const kStylisticAltTenOffSelector: i32 = 21; +pub const kStylisticAltTenOnSelector: i32 = 20; +pub const kStylisticAltThirteenOffSelector: i32 = 27; +pub const kStylisticAltThirteenOnSelector: i32 = 26; +pub const kStylisticAltThreeOffSelector: i32 = 7; +pub const kStylisticAltThreeOnSelector: i32 = 6; +pub const kStylisticAltTwelveOffSelector: i32 = 25; +pub const kStylisticAltTwelveOnSelector: i32 = 24; +pub const kStylisticAltTwentyOffSelector: i32 = 41; +pub const kStylisticAltTwentyOnSelector: i32 = 40; +pub const kStylisticAltTwoOffSelector: i32 = 5; +pub const kStylisticAltTwoOnSelector: i32 = 4; +pub const kStylisticAlternativesType: i32 = 35; +pub const kSwashAlternatesOffSelector: i32 = 3; +pub const kSwashAlternatesOnSelector: i32 = 2; +pub const kThirdWidthTextSelector: i32 = 3; +pub const kTraditionalNamesCharactersSelector: i32 = 14; +pub const kUpperCasePetiteCapsSelector: i32 = 2; +pub const kUpperCaseSmallCapsSelector: i32 = 1; +pub const kUpperCaseType: i32 = 38;