Checkpoint

This commit is contained in:
Nathan Sobo 2023-09-27 15:35:46 -06:00
parent 58dadad8ec
commit e9a84a21e4
11 changed files with 376 additions and 181 deletions

1
Cargo.lock generated
View file

@ -3243,6 +3243,7 @@ dependencies = [
"async-task", "async-task",
"backtrace", "backtrace",
"bindgen 0.65.1", "bindgen 0.65.1",
"bitflags 2.4.0",
"block", "block",
"bytemuck", "bytemuck",
"cbindgen", "cbindgen",

View file

@ -58,6 +58,7 @@ slotmap = "1.0.6"
bytemuck = { version = "1.14.0", features = ["derive"] } bytemuck = { version = "1.14.0", features = ["derive"] }
schemars.workspace = true schemars.workspace = true
plane-split = "0.18.0" plane-split = "0.18.0"
bitflags = "2.4.0"
[dev-dependencies] [dev-dependencies]
backtrace = "0.3" backtrace = "0.3"

View file

@ -13,6 +13,7 @@ use std::{
marker::PhantomData, marker::PhantomData,
sync::{Arc, Weak}, sync::{Arc, Weak},
}; };
use util::ResultExt;
#[derive(Clone)] #[derive(Clone)]
pub struct App(Arc<Mutex<AppContext>>); pub struct App(Arc<Mutex<AppContext>>);
@ -173,7 +174,9 @@ impl AppContext {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for dirty_window_id in dirty_window_ids { for dirty_window_id in dirty_window_ids {
self.update_window(dirty_window_id, |cx| cx.draw()); self.update_window(dirty_window_id, |cx| cx.draw())
.unwrap() // We know we have the window.
.log_err();
} }
} }

View file

@ -77,7 +77,7 @@ impl<T> Flatten<T> for Result<T> {
} }
} }
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq, Hash)]
pub struct SharedString(ArcCow<'static, str>); pub struct SharedString(ArcCow<'static, str>);
impl Default for SharedString { impl Default for SharedString {

View file

@ -6,8 +6,8 @@ mod mac;
mod test; mod test;
use crate::{ use crate::{
AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, GlyphId, AnyWindowHandle, Bounds, FontFeatures, FontId, FontStyle, FontWeight, GlyphId, LineLayout,
LineLayout, Pixels, Point, Result, RunStyle, Scene, SharedString, Size, Pixels, Point, Result, RunStyle, Scene, SharedString, Size,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use async_task::Runnable; use async_task::Runnable;
@ -25,7 +25,6 @@ use std::{
str::FromStr, str::FromStr,
sync::Arc, sync::Arc,
}; };
pub use time::UtcOffset;
use uuid::Uuid; use uuid::Uuid;
pub use events::*; pub use events::*;
@ -34,6 +33,7 @@ pub use keystroke::*;
pub use mac::*; pub use mac::*;
#[cfg(any(test, feature = "test"))] #[cfg(any(test, feature = "test"))]
pub use test::*; pub use test::*;
pub use time::UtcOffset;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub(crate) fn current_platform() -> Arc<dyn Platform> { pub(crate) fn current_platform() -> Arc<dyn Platform> {
@ -154,19 +154,12 @@ pub trait PlatformDispatcher: Send + Sync {
} }
pub trait PlatformTextSystem: Send + Sync { pub trait PlatformTextSystem: Send + Sync {
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()>; fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()>;
fn all_families(&self) -> Vec<String>; fn all_font_families(&self) -> Vec<String>;
fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result<Vec<FontId>>; fn select_font(&self, descriptor: FontDescriptor) -> Result<FontId>;
fn select_font(
&self,
font_ids: &[FontId],
weight: FontWeight,
style: FontStyle,
) -> anyhow::Result<FontId>;
fn font_metrics(&self, font_id: FontId) -> FontMetrics; fn font_metrics(&self, font_id: FontId) -> FontMetrics;
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
-> anyhow::Result<Bounds<f32>>; fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>>;
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
fn rasterize_glyph( fn rasterize_glyph(
&self, &self,
@ -404,3 +397,24 @@ impl ClipboardItem {
hasher.finish() hasher.finish()
} }
} }
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct FontDescriptor {
family: SharedString,
features: FontFeatures,
weight: FontWeight,
style: FontStyle,
}
#[derive(Clone, Copy, Debug)]
pub struct FontMetrics {
pub units_per_em: u32,
pub ascent: f32,
pub descent: f32,
pub line_gap: f32,
pub underline_position: f32,
pub underline_thickness: f32,
pub cap_height: f32,
pub x_height: f32,
pub bounding_box: Bounds<f32>,
}

View file

@ -1,7 +1,5 @@
#![allow(unused, non_upper_case_globals)] #![allow(unused, non_upper_case_globals)]
use std::ptr;
use crate::FontFeatures; use crate::FontFeatures;
use cocoa::appkit::CGFloat; use cocoa::appkit::CGFloat;
use core_foundation::{base::TCFType, number::CFNumber}; use core_foundation::{base::TCFType, number::CFNumber};
@ -13,6 +11,7 @@ use core_text::{
}, },
}; };
use font_kit::font::Font; use font_kit::font::Font;
use std::ptr;
const kCaseSensitiveLayoutOffSelector: i32 = 1; const kCaseSensitiveLayoutOffSelector: i32 = 1;
const kCaseSensitiveLayoutOnSelector: i32 = 0; const kCaseSensitiveLayoutOnSelector: i32 = 0;
@ -108,243 +107,243 @@ const kTypographicExtrasType: i32 = 14;
const kVerticalFractionsSelector: i32 = 1; const kVerticalFractionsSelector: i32 = 1;
const kVerticalPositionType: i32 = 10; const kVerticalPositionType: i32 = 10;
pub fn apply_features(font: &mut Font, features: &FontFeatures) { pub fn apply_features(font: &mut Font, features: FontFeatures) {
// See https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/third_party/harfbuzz-ng/src/hb-coretext.cc // See https://chromium.googlesource.com/chromium/src/+/66.0.3359.158/third_party/harfbuzz-ng/src/hb-coretext.cc
// for a reference implementation. // for a reference implementation.
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.calt, features.calt(),
kContextualAlternatesType, kContextualAlternatesType,
kContextualAlternatesOnSelector, kContextualAlternatesOnSelector,
kContextualAlternatesOffSelector, kContextualAlternatesOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.case, features.case(),
kCaseSensitiveLayoutType, kCaseSensitiveLayoutType,
kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOnSelector,
kCaseSensitiveLayoutOffSelector, kCaseSensitiveLayoutOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.cpsp, features.cpsp(),
kCaseSensitiveLayoutType, kCaseSensitiveLayoutType,
kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOnSelector,
kCaseSensitiveSpacingOffSelector, kCaseSensitiveSpacingOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.frac, features.frac(),
kFractionsType, kFractionsType,
kDiagonalFractionsSelector, kDiagonalFractionsSelector,
kNoFractionsSelector, kNoFractionsSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.liga, features.liga(),
kLigaturesType, kLigaturesType,
kCommonLigaturesOnSelector, kCommonLigaturesOnSelector,
kCommonLigaturesOffSelector, kCommonLigaturesOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.onum, features.onum(),
kNumberCaseType, kNumberCaseType,
kLowerCaseNumbersSelector, kLowerCaseNumbersSelector,
2, 2,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ordn, features.ordn(),
kVerticalPositionType, kVerticalPositionType,
kOrdinalsSelector, kOrdinalsSelector,
kNormalPositionSelector, kNormalPositionSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.pnum, features.pnum(),
kNumberSpacingType, kNumberSpacingType,
kProportionalNumbersSelector, kProportionalNumbersSelector,
4, 4,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss01, features.ss01(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltOneOnSelector, kStylisticAltOneOnSelector,
kStylisticAltOneOffSelector, kStylisticAltOneOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss02, features.ss02(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltTwoOnSelector, kStylisticAltTwoOnSelector,
kStylisticAltTwoOffSelector, kStylisticAltTwoOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss03, features.ss03(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltThreeOnSelector, kStylisticAltThreeOnSelector,
kStylisticAltThreeOffSelector, kStylisticAltThreeOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss04, features.ss04(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltFourOnSelector, kStylisticAltFourOnSelector,
kStylisticAltFourOffSelector, kStylisticAltFourOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss05, features.ss05(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltFiveOnSelector, kStylisticAltFiveOnSelector,
kStylisticAltFiveOffSelector, kStylisticAltFiveOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss06, features.ss06(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltSixOnSelector, kStylisticAltSixOnSelector,
kStylisticAltSixOffSelector, kStylisticAltSixOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss07, features.ss07(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltSevenOnSelector, kStylisticAltSevenOnSelector,
kStylisticAltSevenOffSelector, kStylisticAltSevenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss08, features.ss08(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltEightOnSelector, kStylisticAltEightOnSelector,
kStylisticAltEightOffSelector, kStylisticAltEightOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss09, features.ss09(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltNineOnSelector, kStylisticAltNineOnSelector,
kStylisticAltNineOffSelector, kStylisticAltNineOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss10, features.ss10(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltTenOnSelector, kStylisticAltTenOnSelector,
kStylisticAltTenOffSelector, kStylisticAltTenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss11, features.ss11(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltElevenOnSelector, kStylisticAltElevenOnSelector,
kStylisticAltElevenOffSelector, kStylisticAltElevenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss12, features.ss12(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltTwelveOnSelector, kStylisticAltTwelveOnSelector,
kStylisticAltTwelveOffSelector, kStylisticAltTwelveOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss13, features.ss13(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltThirteenOnSelector, kStylisticAltThirteenOnSelector,
kStylisticAltThirteenOffSelector, kStylisticAltThirteenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss14, features.ss14(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltFourteenOnSelector, kStylisticAltFourteenOnSelector,
kStylisticAltFourteenOffSelector, kStylisticAltFourteenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss15, features.ss15(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltFifteenOnSelector, kStylisticAltFifteenOnSelector,
kStylisticAltFifteenOffSelector, kStylisticAltFifteenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss16, features.ss16(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltSixteenOnSelector, kStylisticAltSixteenOnSelector,
kStylisticAltSixteenOffSelector, kStylisticAltSixteenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss17, features.ss17(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOnSelector,
kStylisticAltSeventeenOffSelector, kStylisticAltSeventeenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss18, features.ss18(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltEighteenOnSelector, kStylisticAltEighteenOnSelector,
kStylisticAltEighteenOffSelector, kStylisticAltEighteenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss19, features.ss19(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltNineteenOnSelector, kStylisticAltNineteenOnSelector,
kStylisticAltNineteenOffSelector, kStylisticAltNineteenOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.ss20, features.ss20(),
kStylisticAlternativesType, kStylisticAlternativesType,
kStylisticAltTwentyOnSelector, kStylisticAltTwentyOnSelector,
kStylisticAltTwentyOffSelector, kStylisticAltTwentyOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.subs, features.subs(),
kVerticalPositionType, kVerticalPositionType,
kInferiorsSelector, kInferiorsSelector,
kNormalPositionSelector, kNormalPositionSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.sups, features.sups(),
kVerticalPositionType, kVerticalPositionType,
kSuperiorsSelector, kSuperiorsSelector,
kNormalPositionSelector, kNormalPositionSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.swsh, features.swsh(),
kContextualAlternatesType, kContextualAlternatesType,
kSwashAlternatesOnSelector, kSwashAlternatesOnSelector,
kSwashAlternatesOffSelector, kSwashAlternatesOffSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.titl, features.titl(),
kStyleOptionsType, kStyleOptionsType,
kTitlingCapsSelector, kTitlingCapsSelector,
kNoStyleOptionsSelector, kNoStyleOptionsSelector,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.tnum, features.tnum(),
kNumberSpacingType, kNumberSpacingType,
kMonospacedNumbersSelector, kMonospacedNumbersSelector,
4, 4,
); );
toggle_open_type_feature( toggle_open_type_feature(
font, font,
features.zero, features.zero(),
kTypographicExtrasType, kTypographicExtrasType,
kSlashedZeroOnSelector, kSlashedZeroOnSelector,
kSlashedZeroOffSelector, kSlashedZeroOffSelector,

View file

@ -1,7 +1,5 @@
use super::ns_string; use super::ns_string;
use crate::{ use crate::{point, px, size, Bounds, Pixels, PlatformScreen, PlatformScreenHandle, ScreenId};
platform, point, px, size, Bounds, Pixels, PlatformScreen, PlatformScreenHandle, ScreenId,
};
use cocoa::{ use cocoa::{
appkit::NSScreen, appkit::NSScreen,
base::{id, nil}, base::{id, nil},

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
point, px, size, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, Glyph, platform::FontDescriptor, point, px, size, Bounds, FontFeatures, FontId, FontMetrics,
GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RasterizationOptions, Run, RunStyle, FontStyle, FontWeight, Glyph, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point,
Size, RasterizationOptions, Result, Run, RunStyle, SharedString, Size,
}; };
use cocoa::appkit::{CGFloat, CGPoint}; use cocoa::appkit::{CGFloat, CGPoint};
use collections::HashMap; use collections::HashMap;
@ -31,6 +31,7 @@ use pathfinder_geometry::{
transform2d::Transform2F, transform2d::Transform2F,
vector::{Vector2F, Vector2I}, vector::{Vector2F, Vector2I},
}; };
use smallvec::SmallVec;
use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc}; use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
use super::open_type; use super::open_type;
@ -44,7 +45,9 @@ struct TextSystemState {
memory_source: MemSource, memory_source: MemSource,
system_source: SystemSource, system_source: SystemSource,
fonts: Vec<font_kit::font::Font>, fonts: Vec<font_kit::font::Font>,
font_selections: HashMap<FontDescriptor, FontId>,
font_ids_by_postscript_name: HashMap<String, FontId>, font_ids_by_postscript_name: HashMap<String, FontId>,
font_ids_by_family_name: HashMap<SharedString, SmallVec<[FontId; 4]>>,
postscript_names_by_font_id: HashMap<FontId, String>, postscript_names_by_font_id: HashMap<FontId, String>,
} }
@ -54,8 +57,10 @@ impl MacTextSystem {
memory_source: MemSource::empty(), memory_source: MemSource::empty(),
system_source: SystemSource::new(), system_source: SystemSource::new(),
fonts: Vec::new(), fonts: Vec::new(),
font_ids_by_postscript_name: Default::default(), font_selections: HashMap::default(),
postscript_names_by_font_id: Default::default(), font_ids_by_postscript_name: HashMap::default(),
font_ids_by_family_name: HashMap::default(),
postscript_names_by_font_id: HashMap::default(),
})) }))
} }
} }
@ -67,11 +72,11 @@ impl Default for MacTextSystem {
} }
impl PlatformTextSystem for MacTextSystem { impl PlatformTextSystem for MacTextSystem {
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()> { fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
self.0.write().add_fonts(fonts) self.0.write().add_fonts(fonts)
} }
fn all_families(&self) -> Vec<String> { fn all_font_families(&self) -> Vec<String> {
self.0 self.0
.read() .read()
.system_source .system_source
@ -79,32 +84,49 @@ impl PlatformTextSystem for MacTextSystem {
.expect("core text should never return an error") .expect("core text should never return an error")
} }
fn load_family(&self, name: &str, features: &FontFeatures) -> anyhow::Result<Vec<FontId>> { fn select_font(&self, descriptor: FontDescriptor) -> Result<FontId> {
self.0.write().load_family(name, features) let lock = self.0.upgradable_read();
} if let Some(font_id) = lock.font_selections.get(&descriptor) {
Ok(*font_id)
} else {
let mut lock = parking_lot::RwLockUpgradableReadGuard::upgrade(lock);
let candidates =
if let Some(font_ids) = lock.font_ids_by_family_name.get(&descriptor.family) {
font_ids.as_slice()
} else {
let font_ids = lock.load_family(&descriptor.family, descriptor.features)?;
lock.font_ids_by_family_name
.insert(descriptor.family.clone(), font_ids);
lock.font_ids_by_family_name[&descriptor.family].as_ref()
};
fn select_font( let candidate_properties = candidates
&self, .iter()
font_ids: &[FontId], .map(|font_id| lock.fonts[font_id.0].properties())
weight: FontWeight, .collect::<SmallVec<[_; 4]>>();
style: FontStyle,
) -> anyhow::Result<FontId> { let ix = font_kit::matching::find_best_match(
self.0.read().select_font(font_ids, weight, style) &candidate_properties,
&font_kit::properties::Properties {
style: descriptor.style.into(),
weight: descriptor.weight.into(),
stretch: Default::default(),
},
)?;
Ok(candidates[ix])
}
} }
fn font_metrics(&self, font_id: FontId) -> FontMetrics { fn font_metrics(&self, font_id: FontId) -> FontMetrics {
self.0.read().font_metrics(font_id) self.0.read().font_metrics(font_id)
} }
fn typographic_bounds( fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
&self,
font_id: FontId,
glyph_id: GlyphId,
) -> anyhow::Result<Bounds<f32>> {
self.0.read().typographic_bounds(font_id, glyph_id) self.0.read().typographic_bounds(font_id, glyph_id)
} }
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> { fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
self.0.read().advance(font_id, glyph_id) self.0.read().advance(font_id, glyph_id)
} }
@ -147,7 +169,7 @@ impl PlatformTextSystem for MacTextSystem {
} }
impl TextSystemState { impl TextSystemState {
fn add_fonts(&mut self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()> { fn add_fonts(&mut self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
self.memory_source.add_fonts( self.memory_source.add_fonts(
fonts fonts
.iter() .iter()
@ -156,13 +178,16 @@ impl TextSystemState {
Ok(()) Ok(())
} }
fn load_family(&mut self, name: &str, features: &FontFeatures) -> anyhow::Result<Vec<FontId>> { fn load_family(
let mut font_ids = Vec::new(); &mut self,
name: &SharedString,
features: FontFeatures,
) -> Result<SmallVec<[FontId; 4]>> {
let mut font_ids = SmallVec::new();
let family = self let family = self
.memory_source .memory_source
.select_family_by_name(name) .select_family_by_name(name.as_ref())
.or_else(|_| self.system_source.select_family_by_name(name))?; .or_else(|_| self.system_source.select_family_by_name(name.as_ref()))?;
for font in family.fonts() { for font in family.fonts() {
let mut font = font.load()?; let mut font = font.load()?;
open_type::apply_features(&mut font, features); open_type::apply_features(&mut font, features);
@ -178,42 +203,53 @@ impl TextSystemState {
Ok(font_ids) Ok(font_ids)
} }
fn select_font( // fn select_font(
&self, // &mut self,
font_ids: &[FontId], // family: &SharedString,
weight: FontWeight, // weight: FontWeight,
style: FontStyle, // style: FontStyle,
) -> anyhow::Result<FontId> { // features: FontFeatures,
let candidates = font_ids // ) -> Result<FontId> {
.iter() // let candidates = if let Some(font_ids) = self.font_ids_by_family_name.get(family) {
.map(|font_id| self.fonts[font_id.0].properties()) // font_ids
.collect::<Vec<_>>(); // } else {
let idx = font_kit::matching::find_best_match( // let font_ids = if let Some(font_ids) = self.font_ids_by_family_name.get(family) {
&candidates, // font_ids.as_slice()
&font_kit::properties::Properties { // } else {
style: style.into(), // self.font_ids_by_family_name
weight: weight.into(), // .insert(family.clone())
stretch: Default::default(), // .or_insert(font_ids).as_slice()
}, // };
)?;
Ok(font_ids[idx]) // };
}
// let font_properties = candidates
// .iter()
// .map(|font_id| self.fonts[font_id.0].properties())
// .collect::<SmallVec<[_; 4]>>();
// // let idx = font_kit::matching::find_best_match(
// // &candidates,
// // &font_kit::properties::Properties {
// // style: style.into(),
// // weight: weight.into(),
// // stretch: Default::default(),
// // },
// // )?;
// // Ok(font_ids[idx])
// }
fn font_metrics(&self, font_id: FontId) -> FontMetrics { fn font_metrics(&self, font_id: FontId) -> FontMetrics {
self.fonts[font_id.0].metrics().into() self.fonts[font_id.0].metrics().into()
} }
fn typographic_bounds( fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
&self,
font_id: FontId,
glyph_id: GlyphId,
) -> anyhow::Result<Bounds<f32>> {
Ok(self.fonts[font_id.0] Ok(self.fonts[font_id.0]
.typographic_bounds(glyph_id.into())? .typographic_bounds(glyph_id.into())?
.into()) .into())
} }
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> { fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
Ok(self.fonts[font_id.0].advance(glyph_id.into())?.into()) Ok(self.fonts[font_id.0].advance(glyph_id.into())?.into())
} }
@ -659,7 +695,7 @@ impl From<FontStyle> for FontkitStyle {
// } // }
// #[test] // #[test]
// fn test_glyph_offsets() -> anyhow::Result<()> { // fn test_glyph_offsets() -> crate::Result<()> {
// let fonts = FontSystem::new(); // let fonts = FontSystem::new();
// let zapfino = fonts.load_family("Zapfino", &Default::default())?; // let zapfino = fonts.load_family("Zapfino", &Default::default())?;
// let zapfino_regular = RunStyle { // let zapfino_regular = RunStyle {

View file

@ -1,11 +1,9 @@
mod font_cache; mod font_features;
mod line_wrapper; mod line_wrapper;
mod text_layout_cache; mod text_layout_cache;
pub use font_cache::*; pub use font_features::*;
use line_wrapper::*; use line_wrapper::*;
use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
pub use text_layout_cache::*; pub use text_layout_cache::*;
use crate::{Hsla, Pixels, PlatformTextSystem, Point, Result, Size, UnderlineStyle}; use crate::{Hsla, Pixels, PlatformTextSystem, Point, Result, Size, UnderlineStyle};
@ -19,8 +17,13 @@ use std::{
sync::Arc, sync::Arc,
}; };
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
pub struct FontId(pub usize);
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
pub struct FontFamilyId(pub usize);
pub struct TextSystem { pub struct TextSystem {
font_cache: Arc<FontCache>,
text_layout_cache: Arc<TextLayoutCache>, text_layout_cache: Arc<TextLayoutCache>,
platform_text_system: Arc<dyn PlatformTextSystem>, platform_text_system: Arc<dyn PlatformTextSystem>,
wrapper_pool: Mutex<HashMap<(FontId, Pixels), Vec<LineWrapper>>>, wrapper_pool: Mutex<HashMap<(FontId, Pixels), Vec<LineWrapper>>>,
@ -29,7 +32,7 @@ pub struct TextSystem {
impl TextSystem { impl TextSystem {
pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self { pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
TextSystem { TextSystem {
font_cache: Arc::new(FontCache::new(platform_text_system.clone())), // font_cache: Arc::new(FontCache::new(platform_text_system.clone())),
text_layout_cache: Arc::new(TextLayoutCache::new(platform_text_system.clone())), text_layout_cache: Arc::new(TextLayoutCache::new(platform_text_system.clone())),
platform_text_system, platform_text_system,
wrapper_pool: Mutex::new(HashMap::default()), wrapper_pool: Mutex::new(HashMap::default()),
@ -37,7 +40,8 @@ impl TextSystem {
} }
pub fn font_family_name(&self, family_id: FontFamilyId) -> Result<Arc<str>> { pub fn font_family_name(&self, family_id: FontFamilyId) -> Result<Arc<str>> {
self.font_cache.family_name(family_id) todo!()
// self.font_cache.family_name(family_id)
} }
pub fn load_font_family( pub fn load_font_family(
@ -45,16 +49,19 @@ impl TextSystem {
names: &[&str], names: &[&str],
features: &FontFeatures, features: &FontFeatures,
) -> Result<FontFamilyId> { ) -> Result<FontFamilyId> {
self.font_cache.load_family(names, features) todo!()
// self.font_cache.load_family(names, features)
} }
/// Returns an arbitrary font family that is available on the system. /// Returns an arbitrary font family that is available on the system.
pub fn known_existing_font_family(&self) -> FontFamilyId { pub fn known_existing_font_family(&self) -> FontFamilyId {
self.font_cache.known_existing_family() todo!()
// self.font_cache.known_existing_family()
} }
pub fn default_font(&self, family_id: FontFamilyId) -> FontId { pub fn default_font(&self, family_id: FontFamilyId) -> FontId {
self.font_cache.default_font(family_id) todo!()
// self.font_cache.default_font(family_id)
} }
pub fn select_font( pub fn select_font(
@ -63,55 +70,67 @@ impl TextSystem {
weight: FontWeight, weight: FontWeight,
style: FontStyle, style: FontStyle,
) -> Result<FontId> { ) -> Result<FontId> {
self.font_cache.select_font(family_id, weight, style) todo!()
// self.font_cache.select_font(family_id, weight, style)
} }
pub fn read_font_metric<F, T>(&self, font_id: FontId, f: F) -> T // pub fn read_font_metric<F, T>(&self, font_id: FontId, f: F) -> T
where // where
F: FnOnce(&FontMetrics) -> T, // F: FnOnce(&FontMetrics) -> T,
T: 'static, // T: 'static,
{ // {
self.font_cache.read_metric(font_id, f) // todo!()
} // // self.font_cache.read_metric(font_id, f)
// }
pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Size<Pixels> { pub fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Size<Pixels> {
self.font_cache.bounding_box(font_id, font_size) todo!()
// self.font_cache.bounding_box(font_id, font_size)
} }
pub fn em_width(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn em_width(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.em_width(font_id, font_size) todo!()
// self.font_cache.em_width(font_id, font_size)
} }
pub fn em_advance(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn em_advance(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.em_advance(font_id, font_size) todo!()
// self.font_cache.em_advance(font_id, font_size)
} }
pub fn line_height(&self, font_size: Pixels) -> Pixels { pub fn line_height(&self, font_size: Pixels) -> Pixels {
self.font_cache.line_height(font_size) todo!()
// self.font_cache.line_height(font_size)
} }
pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.cap_height(font_id, font_size) todo!()
// self.font_cache.cap_height(font_id, font_size)
} }
pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.x_height(font_id, font_size) todo!()
// self.font_cache.x_height(font_id, font_size)
} }
pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.ascent(font_id, font_size) todo!()
// self.font_cache.ascent(font_id, font_size)
} }
pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.descent(font_id, font_size) todo!()
// self.font_cache.descent(font_id, font_size)
} }
pub fn em_size(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn em_size(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.em_size(font_id, font_size) todo!()
// self.font_cache.em_size(font_id, font_size)
} }
pub fn baseline_offset(&self, font_id: FontId, font_size: Pixels) -> Pixels { pub fn baseline_offset(&self, font_id: FontId, font_size: Pixels) -> Pixels {
self.font_cache.baseline_offset(font_id, font_size) todo!()
// self.font_cache.baseline_offset(font_id, font_size)
} }
pub fn layout_str<'a>( pub fn layout_str<'a>(
@ -235,44 +254,6 @@ impl Display for FontStyle {
} }
} }
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct FontFeatures {
pub calt: Option<bool>,
pub case: Option<bool>,
pub cpsp: Option<bool>,
pub frac: Option<bool>,
pub liga: Option<bool>,
pub onum: Option<bool>,
pub ordn: Option<bool>,
pub pnum: Option<bool>,
pub ss01: Option<bool>,
pub ss02: Option<bool>,
pub ss03: Option<bool>,
pub ss04: Option<bool>,
pub ss05: Option<bool>,
pub ss06: Option<bool>,
pub ss07: Option<bool>,
pub ss08: Option<bool>,
pub ss09: Option<bool>,
pub ss10: Option<bool>,
pub ss11: Option<bool>,
pub ss12: Option<bool>,
pub ss13: Option<bool>,
pub ss14: Option<bool>,
pub ss15: Option<bool>,
pub ss16: Option<bool>,
pub ss17: Option<bool>,
pub ss18: Option<bool>,
pub ss19: Option<bool>,
pub ss20: Option<bool>,
pub subs: Option<bool>,
pub sups: Option<bool>,
pub swsh: Option<bool>,
pub titl: Option<bool>,
pub tnum: Option<bool>,
pub zero: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct RunStyle { pub struct RunStyle {
pub color: Hsla, pub color: Hsla,

View file

@ -0,0 +1,162 @@
use schemars::{
schema::{InstanceType, Schema, SchemaObject, SingleOrVec},
JsonSchema,
};
macro_rules! create_definitions {
($($(#[$meta:meta])* ($name:ident, $idx:expr)),* $(,)?) => {
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct FontFeatures {
enabled: u64,
disabled: u64,
}
impl FontFeatures {
$(
pub fn $name(&self) -> Option<bool> {
if (self.enabled & (1 << $idx)) != 0 {
Some(true)
} else if (self.disabled & (1 << $idx)) != 0 {
Some(false)
} else {
None
}
}
)*
}
impl std::fmt::Debug for FontFeatures {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("FontFeatures");
$(
if let Some(value) = self.$name() {
debug.field(stringify!($name), &value);
};
)*
debug.finish()
}
}
impl<'de> serde::Deserialize<'de> for FontFeatures {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{MapAccess, Visitor};
use std::fmt;
struct FontFeaturesVisitor;
impl<'de> Visitor<'de> for FontFeaturesVisitor {
type Value = FontFeatures;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map of font features")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut enabled: u64 = 0;
let mut disabled: u64 = 0;
while let Some((key, value)) = access.next_entry::<String, Option<bool>>()? {
let idx = match key.as_str() {
$(stringify!($name) => $idx,)*
_ => continue,
};
match value {
Some(true) => enabled |= 1 << idx,
Some(false) => disabled |= 1 << idx,
None => {}
};
}
Ok(FontFeatures { enabled, disabled })
}
}
let features = deserializer.deserialize_map(FontFeaturesVisitor)?;
Ok(features)
}
}
impl serde::Serialize for FontFeatures {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(None)?;
$(
let feature = stringify!($name);
if let Some(value) = self.$name() {
map.serialize_entry(feature, &value)?;
}
)*
map.end()
}
}
impl JsonSchema for FontFeatures {
fn schema_name() -> String {
"FontFeatures".into()
}
fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> Schema {
let mut schema = SchemaObject::default();
let properties = &mut schema.object().properties;
let feature_schema = Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Boolean))),
..Default::default()
});
$(
properties.insert(stringify!($name).to_owned(), feature_schema.clone());
)*
schema.into()
}
}
};
}
create_definitions!(
(calt, 0),
(case, 1),
(cpsp, 2),
(frac, 3),
(liga, 4),
(onum, 5),
(ordn, 6),
(pnum, 7),
(ss01, 8),
(ss02, 9),
(ss03, 10),
(ss04, 11),
(ss05, 12),
(ss06, 13),
(ss07, 14),
(ss08, 15),
(ss09, 16),
(ss10, 17),
(ss11, 18),
(ss12, 19),
(ss13, 20),
(ss14, 21),
(ss15, 22),
(ss16, 23),
(ss17, 24),
(ss18, 25),
(ss19, 26),
(ss20, 27),
(subs, 28),
(sups, 29),
(swsh, 30),
(titl, 31),
(tnum, 32),
(zero, 33)
);

View file

@ -215,7 +215,7 @@ impl Boundary {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{App, FontWeight}; use crate::{App, FontFeatures, FontWeight};
#[test] #[test]
fn test_wrap_line() { fn test_wrap_line() {
@ -291,7 +291,7 @@ mod tests {
let text_system = cx.text_system().clone(); let text_system = cx.text_system().clone();
let family = text_system let family = text_system
.load_font_family(&["Helvetica"], &Default::default()) .load_font_family(&["Helvetica"], &FontFeatures::default())
.unwrap(); .unwrap();
let font_id = text_system let font_id = text_system
.select_font(family, Default::default(), Default::default()) .select_font(family, Default::default(), Default::default())