diff --git a/Cargo.lock b/Cargo.lock index 222083a8..c9d648c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1815,6 +1815,7 @@ dependencies = [ name = "parley" version = "0.2.0" dependencies = [ + "core_maths", "fontique", "peniko", "skrifa", diff --git a/fontique/src/attributes.rs b/fontique/src/attributes.rs index 9ab3820d..522c8016 100644 --- a/fontique/src/attributes.rs +++ b/fontique/src/attributes.rs @@ -3,10 +3,7 @@ //! Properties for specifying font weight, stretch and style. -// This is unused due to an issue in CI and the fact that our -// no_std support doesn't really work correctly yet. -// See https://github.com/linebender/parley/issues/86 -#[cfg(not(feature = "std"))] +#[cfg(feature = "libm")] #[allow(unused_imports)] use core_maths::*; diff --git a/fontique/src/collection/mod.rs b/fontique/src/collection/mod.rs index fce20275..76a6c211 100644 --- a/fontique/src/collection/mod.rs +++ b/fontique/src/collection/mod.rs @@ -7,7 +7,6 @@ mod query; pub use query::{Query, QueryFamily, QueryFont, QueryStatus}; -#[cfg(feature = "std")] use super::SourceCache; use super::{ @@ -166,7 +165,6 @@ impl Collection { } /// Returns an object for selecting fonts from this collection. - #[cfg(feature = "std")] pub fn query<'a>(&'a mut self, source_cache: &'a mut SourceCache) -> Query<'a> { Query::new(self, source_cache) } diff --git a/fontique/src/collection/query.rs b/fontique/src/collection/query.rs index 48e42b42..895b9946 100644 --- a/fontique/src/collection/query.rs +++ b/fontique/src/collection/query.rs @@ -3,10 +3,8 @@ //! Query support. -#[cfg(feature = "std")] use super::super::{Collection, SourceCache}; -#[cfg(not(feature = "std"))] use alloc::vec::Vec; use super::{ @@ -33,14 +31,12 @@ impl QueryState { pub struct Query<'a> { collection: &'a mut Inner, state: &'a mut QueryState, - #[cfg(feature = "std")] source_cache: &'a mut SourceCache, attributes: Attributes, fallbacks: Option, } impl<'a> Query<'a> { - #[cfg(feature = "std")] pub(super) fn new(collection: &'a mut Collection, source_cache: &'a mut SourceCache) -> Self { collection.query_state.clear(); Self { @@ -108,7 +104,6 @@ impl<'a> Query<'a> { /// /// Return [`QueryStatus::Stop`] to end iterating over the matching /// fonts or [`QueryStatus::Continue`] to continue iterating. - #[cfg(feature = "std")] pub fn matches_with(&mut self, mut f: impl FnMut(&QueryFont) -> QueryStatus) { for family in self .state @@ -226,7 +221,6 @@ pub struct QueryFont { pub synthesis: Synthesis, } -#[cfg(feature = "std")] fn load_font<'a>( family: &FamilyInfo, attributes: Attributes, diff --git a/fontique/src/font.rs b/fontique/src/font.rs index b566eddc..d60450aa 100644 --- a/fontique/src/font.rs +++ b/fontique/src/font.rs @@ -5,7 +5,6 @@ use super::attributes::{Stretch, Style, Weight}; use super::source::{SourceInfo, SourceKind}; -#[cfg(feature = "std")] use super::{source_cache::SourceCache, Blob}; use skrifa::raw::{types::Tag, FontRef, TableProvider as _}; use smallvec::SmallVec; @@ -54,13 +53,13 @@ impl FontInfo { } /// Attempts to load the font, optionally from a source cache. - #[cfg(feature = "std")] pub fn load(&self, source_cache: Option<&mut SourceCache>) -> Option> { if let Some(source_cache) = source_cache { source_cache.get(&self.source) } else { match &self.source.kind { SourceKind::Memory(blob) => Some(blob.clone()), + #[cfg(feature = "std")] SourceKind::Path(path) => super::source_cache::load_blob(path), } } diff --git a/fontique/src/lib.rs b/fontique/src/lib.rs index d5892be3..daf1de67 100644 --- a/fontique/src/lib.rs +++ b/fontique/src/lib.rs @@ -4,9 +4,7 @@ //! Font enumeration and fallback. #![cfg_attr(docsrs, feature(doc_auto_cfg))] -// TODO: Remove this dead code allowance and hide the offending code behind the std feature gate. -#![cfg_attr(not(feature = "std"), allow(dead_code))] -#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] +#![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(any(feature = "std", feature = "libm")))] compile_error!("fontique requires either the `std` or `libm` feature to be enabled"); @@ -26,7 +24,6 @@ mod scan; mod script; mod source; -#[cfg(feature = "std")] mod source_cache; pub use icu_locid::LanguageIdentifier as Language; @@ -41,5 +38,4 @@ pub use generic::GenericFamily; pub use script::Script; pub use source::{SourceId, SourceInfo, SourceKind}; -#[cfg(feature = "std")] pub use source_cache::{SourceCache, SourceCacheOptions}; diff --git a/fontique/src/scan.rs b/fontique/src/scan.rs index b6bcfc7f..4884a02d 100644 --- a/fontique/src/scan.rs +++ b/fontique/src/scan.rs @@ -17,7 +17,6 @@ use smallvec::SmallVec; #[cfg(feature = "std")] use {super::source::SourcePathMap, std::path::Path}; -#[cfg(not(feature = "std"))] use alloc::vec::Vec; #[cfg(feature = "std")] diff --git a/fontique/src/source_cache.rs b/fontique/src/source_cache.rs index 6b92de44..d66aac60 100644 --- a/fontique/src/source_cache.rs +++ b/fontique/src/source_cache.rs @@ -3,9 +3,15 @@ //! Cache for font data. -use super::source::{SourceId, SourceInfo, SourceKind}; +#[cfg(feature = "std")] +use super::source::SourceId; +use super::source::{SourceInfo, SourceKind}; +#[cfg(feature = "std")] use hashbrown::HashMap; -use peniko::{Blob, WeakBlob}; +use peniko::Blob; +#[cfg(feature = "std")] +use peniko::WeakBlob; +#[cfg(feature = "std")] use std::{ path::Path, sync::{Arc, Mutex}, @@ -16,6 +22,7 @@ use std::{ /// [source cache]: SourceCache #[derive(Copy, Clone, Default, Debug)] pub struct SourceCacheOptions { + #[cfg(feature = "std")] /// If true, the source cache will use a secondary shared cache /// guaranteeing that all clones will use the same backing store. /// @@ -29,8 +36,11 @@ pub struct SourceCacheOptions { /// Cache for font data loaded from the file system. #[derive(Clone, Default)] pub struct SourceCache { + #[cfg(feature = "std")] cache: HashMap>>, + #[cfg(feature = "std")] serial: u64, + #[cfg(feature = "std")] shared: Option>>, } @@ -38,16 +48,17 @@ impl SourceCache { /// Creates an empty cache with the given [options]. /// /// [options]: SourceCacheOptions + #[cfg_attr(not(feature = "std"), allow(unused))] pub fn new(options: SourceCacheOptions) -> Self { + #[cfg(feature = "std")] if options.shared { - Self { + return Self { cache: Default::default(), serial: 0, shared: Some(Arc::new(Mutex::new(Shared::default()))), - } - } else { - Self::default() + }; } + Self::default() } /// Creates an empty cache that is suitable for multi-threaded use. @@ -57,6 +68,7 @@ impl SourceCache { /// /// This is the same as calling [`SourceCache::new`] with an options /// struct where `shared = true`. + #[cfg(feature = "std")] pub fn new_shared() -> Self { Self { cache: Default::default(), @@ -72,49 +84,52 @@ impl SourceCache { /// /// [blob]: Blob pub fn get(&mut self, source: &SourceInfo) -> Option> { - let path = match &source.kind { - SourceKind::Memory(memory) => return Some(memory.clone()), - SourceKind::Path(path) => &**path, - }; - use hashbrown::hash_map::Entry as HashEntry; - match self.cache.entry(source.id()) { - HashEntry::Vacant(vacant) => { - if let Some(mut shared) = self.shared.as_ref().and_then(|shared| shared.lock().ok()) - { - // If we have a backing cache, try to load it there first - // and then propagate the result here. - if let Some(blob) = shared.get(source.id(), path) { - vacant.insert(Entry::Loaded(EntryData { - font_data: blob.clone(), - serial: self.serial, - })); - Some(blob) - } else { - vacant.insert(Entry::Failed); - None - } - } else { - // Otherwise, load it ourselves. - if let Some(blob) = load_blob(path) { - vacant.insert(Entry::Loaded(EntryData { - font_data: blob.clone(), - serial: self.serial, - })); - Some(blob) - } else { - vacant.insert(Entry::Failed); - None + match &source.kind { + SourceKind::Memory(memory) => Some(memory.clone()), + #[cfg(feature = "std")] + SourceKind::Path(path) => { + use hashbrown::hash_map::Entry as HashEntry; + match self.cache.entry(source.id()) { + HashEntry::Vacant(vacant) => { + if let Some(mut shared) = + self.shared.as_ref().and_then(|shared| shared.lock().ok()) + { + // If we have a backing cache, try to load it there first + // and then propagate the result here. + if let Some(blob) = shared.get(source.id(), path) { + vacant.insert(Entry::Loaded(EntryData { + font_data: blob.clone(), + serial: self.serial, + })); + Some(blob) + } else { + vacant.insert(Entry::Failed); + None + } + } else { + // Otherwise, load it ourselves. + if let Some(blob) = load_blob(path) { + vacant.insert(Entry::Loaded(EntryData { + font_data: blob.clone(), + serial: self.serial, + })); + Some(blob) + } else { + vacant.insert(Entry::Failed); + None + } + } } - } - } - HashEntry::Occupied(mut occupied) => { - let entry = occupied.get_mut(); - match entry { - Entry::Loaded(data) => { - data.serial = self.serial; - Some(data.font_data.clone()) + HashEntry::Occupied(mut occupied) => { + let entry = occupied.get_mut(); + match entry { + Entry::Loaded(data) => { + data.serial = self.serial; + Some(data.font_data.clone()) + } + Entry::Failed => None, + } } - Entry::Failed => None, } } } @@ -122,21 +137,27 @@ impl SourceCache { /// Removes all cached blobs that have not been accessed in the last /// `max_age` times `prune` has been called. + #[cfg_attr(not(feature = "std"), allow(unused))] pub fn prune(&mut self, max_age: u64, prune_failed: bool) { - self.cache.retain(|_, entry| match entry { - Entry::Failed => !prune_failed, - Entry::Loaded(data) => self.serial.saturating_sub(data.serial) < max_age, - }); - self.serial = self.serial.saturating_add(1); + #[cfg(feature = "std")] + { + self.cache.retain(|_, entry| match entry { + Entry::Failed => !prune_failed, + Entry::Loaded(data) => self.serial.saturating_sub(data.serial) < max_age, + }); + self.serial = self.serial.saturating_add(1); + } } } /// Shared backing store for a font data cache. +#[cfg(feature = "std")] #[derive(Default)] struct Shared { cache: HashMap>>, } +#[cfg(feature = "std")] impl Shared { pub fn get(&mut self, id: SourceId, path: &Path) -> Option> { use hashbrown::hash_map::Entry as HashEntry; @@ -177,6 +198,7 @@ impl Shared { } } +#[cfg(feature = "std")] #[derive(Clone, Default)] enum Entry { Loaded(EntryData), @@ -184,12 +206,14 @@ enum Entry { Failed, } +#[cfg(feature = "std")] #[derive(Clone)] struct EntryData { font_data: T, serial: u64, } +#[cfg(feature = "std")] pub(crate) fn load_blob(path: &Path) -> Option> { let file = std::fs::File::open(path).ok()?; let mapped = unsafe { memmap2::Mmap::map(&file).ok()? }; diff --git a/parley/Cargo.toml b/parley/Cargo.toml index 3face0fc..425c9285 100644 --- a/parley/Cargo.toml +++ b/parley/Cargo.toml @@ -18,7 +18,7 @@ workspace = true [features] default = ["system"] std = ["fontique/std", "skrifa/std", "peniko/std"] -libm = ["fontique/libm", "skrifa/libm", "peniko/libm"] +libm = ["fontique/libm", "skrifa/libm", "peniko/libm", "dep:core_maths"] # Enables support for system font backends system = ["std", "fontique/system"] @@ -27,3 +27,4 @@ swash = { workspace = true } skrifa = { workspace = true } peniko = { workspace = true } fontique = { workspace = true } +core_maths = { version = "0.1.0", optional = true } diff --git a/parley/src/bidi.rs b/parley/src/bidi.rs index daffa820..a3ce4e13 100644 --- a/parley/src/bidi.rs +++ b/parley/src/bidi.rs @@ -3,7 +3,6 @@ //! Unicode bidirectional algorithm. -#[cfg(not(feature = "std"))] use alloc::vec::Vec; use swash::text::{BidiClass, BracketType, Codepoint as _}; diff --git a/parley/src/builder.rs b/parley/src/builder.rs index ceec9872..90d2fb68 100644 --- a/parley/src/builder.rs +++ b/parley/src/builder.rs @@ -7,9 +7,9 @@ use super::context::*; use super::style::*; use super::FontContext; -#[cfg(feature = "std")] use super::layout::Layout; +use alloc::string::String; use core::ops::RangeBounds; use crate::inline_box::InlineBox; @@ -46,7 +46,6 @@ impl RangedBuilder<'_, B> { self.lcx.inline_boxes.push(inline_box); } - #[cfg(feature = "std")] pub fn build_into(&mut self, layout: &mut Layout, text: impl AsRef) { // Apply RangedStyleBuilder styles to LayoutContext self.lcx.ranged_style_builder.finish(&mut self.lcx.styles); @@ -55,7 +54,6 @@ impl RangedBuilder<'_, B> { build_into_layout(layout, self.scale, text.as_ref(), self.lcx, self.fcx); } - #[cfg(feature = "std")] pub fn build(&mut self, text: impl AsRef) -> Layout { let mut layout = Layout::default(); self.build_into(&mut layout, text); @@ -114,7 +112,6 @@ impl TreeBuilder<'_, B> { .set_white_space_mode(white_space_collapse); } - #[cfg(feature = "std")] pub fn build_into(&mut self, layout: &mut Layout) -> String { // Apply TreeStyleBuilder styles to LayoutContext let text = self.lcx.tree_style_builder.finish(&mut self.lcx.styles); @@ -127,7 +124,6 @@ impl TreeBuilder<'_, B> { text } - #[cfg(feature = "std")] pub fn build(&mut self) -> (Layout, String) { let mut layout = Layout::default(); let text = self.build_into(&mut layout); @@ -135,7 +131,6 @@ impl TreeBuilder<'_, B> { } } -#[cfg(feature = "std")] fn build_into_layout( layout: &mut Layout, scale: f32, diff --git a/parley/src/context.rs b/parley/src/context.rs index 418ed34d..007ea49f 100644 --- a/parley/src/context.rs +++ b/parley/src/context.rs @@ -3,7 +3,6 @@ //! Context for layout. -#[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; use self::tree::TreeStyleBuilder; @@ -80,7 +79,6 @@ impl LayoutContext { self.analyze_text(text); self.ranged_style_builder.begin(text.len()); - #[cfg(feature = "std")] fcx.source_cache.prune(128, false); RangedBuilder { @@ -101,7 +99,6 @@ impl LayoutContext { let resolved_root_style = self.resolve_style_set(fcx, scale, raw_style); self.tree_style_builder.begin(resolved_root_style); - #[cfg(feature = "std")] fcx.source_cache.prune(128, false); TreeBuilder { diff --git a/parley/src/font.rs b/parley/src/font.rs index efde37bf..02aba3f2 100644 --- a/parley/src/font.rs +++ b/parley/src/font.rs @@ -3,7 +3,6 @@ use fontique::Collection; -#[cfg(feature = "std")] use fontique::SourceCache; /// A font database/cache (wrapper around a Fontique [`Collection`] and [`SourceCache`]). @@ -12,7 +11,6 @@ use fontique::SourceCache; #[derive(Default, Clone)] pub struct FontContext { pub collection: Collection, - #[cfg(feature = "std")] pub source_cache: SourceCache, } diff --git a/parley/src/layout/data.rs b/parley/src/layout/data.rs index 50ffb400..0be51109 100644 --- a/parley/src/layout/data.rs +++ b/parley/src/layout/data.rs @@ -11,7 +11,6 @@ use swash::shape::Shaper; use swash::text::cluster::{Boundary, ClusterInfo}; use swash::Synthesis; -#[cfg(not(feature = "std"))] use alloc::vec::Vec; #[derive(Copy, Clone)] diff --git a/parley/src/layout/editor.rs b/parley/src/layout/editor.rs index d07453b2..193a0d96 100644 --- a/parley/src/layout/editor.rs +++ b/parley/src/layout/editor.rs @@ -11,7 +11,7 @@ use crate::{ style::{Brush, StyleProperty}, FontContext, LayoutContext, Rect, }; -use alloc::{sync::Arc, vec::Vec}; +use alloc::{borrow::ToOwned, string::String, sync::Arc, vec::Vec}; #[derive(Copy, Clone, Debug)] pub enum ActiveText<'a> { diff --git a/parley/src/layout/line/greedy.rs b/parley/src/layout/line/greedy.rs index f34f657f..1f5e7d3b 100644 --- a/parley/src/layout/line/greedy.rs +++ b/parley/src/layout/line/greedy.rs @@ -3,9 +3,12 @@ //! Greedy line breaking. -#[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(feature = "libm")] +#[allow(unused_imports)] +use core_maths::*; + use crate::layout::alignment::unjustify; use crate::layout::*; use crate::style::Brush; diff --git a/parley/src/layout/mod.rs b/parley/src/layout/mod.rs index f3547996..b6b5fc17 100644 --- a/parley/src/layout/mod.rs +++ b/parley/src/layout/mod.rs @@ -12,9 +12,6 @@ pub(crate) mod data; pub mod cursor; -// TODO: make editor `no_std` capable. -// `std` required only because of `RangedEditor::build_into`. -#[cfg(feature = "std")] pub mod editor; use self::alignment::align; diff --git a/parley/src/lib.rs b/parley/src/lib.rs index 7d13e8f9..3c59f54a 100644 --- a/parley/src/lib.rs +++ b/parley/src/lib.rs @@ -73,9 +73,7 @@ //! ``` #![cfg_attr(docsrs, feature(doc_auto_cfg))] -// TODO: Remove this dead code allowance and hide the offending code behind the std feature gate. -#![cfg_attr(not(feature = "std"), allow(dead_code))] -#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] +#![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(any(feature = "std", feature = "libm")))] compile_error!("parley requires either the `std` or `libm` feature to be enabled"); @@ -108,7 +106,6 @@ pub use inline_box::InlineBox; #[doc(inline)] pub use layout::Layout; -#[cfg(feature = "std")] pub use layout::editor::{PlainEditor, PlainEditorTxn}; pub use layout::*; diff --git a/parley/src/resolve/mod.rs b/parley/src/resolve/mod.rs index 055b21c5..b2db69b7 100644 --- a/parley/src/resolve/mod.rs +++ b/parley/src/resolve/mod.rs @@ -8,7 +8,6 @@ pub mod tree; pub use range::RangedStyleBuilder; -#[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; use super::style::{ diff --git a/parley/src/resolve/range.rs b/parley/src/resolve/range.rs index ceb25932..ce1bb971 100644 --- a/parley/src/resolve/range.rs +++ b/parley/src/resolve/range.rs @@ -3,7 +3,6 @@ //! Range based style application. -#[cfg(not(feature = "std"))] use alloc::vec; use super::*; diff --git a/parley/src/resolve/tree.rs b/parley/src/resolve/tree.rs index dbe88846..fc9938bd 100644 --- a/parley/src/resolve/tree.rs +++ b/parley/src/resolve/tree.rs @@ -3,7 +3,6 @@ //! Hierarchical tree based style application. use alloc::borrow::Cow; -#[cfg(not(feature = "std"))] use alloc::{string::String, vec::Vec}; use crate::style::WhiteSpaceCollapse; diff --git a/parley/src/shape.rs b/parley/src/shape.rs index 1711179b..633e0c05 100644 --- a/parley/src/shape.rs +++ b/parley/src/shape.rs @@ -1,24 +1,20 @@ // Copyright 2021 the Parley Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(feature = "std")] use super::layout::Layout; use super::resolve::{RangedStyle, ResolveContext, Resolved}; use super::style::{Brush, FontFeature, FontVariation}; -#[cfg(feature = "std")] use crate::util::nearly_eq; -#[cfg(feature = "std")] use crate::Font; -#[cfg(feature = "std")] use fontique::QueryFamily; use fontique::{self, Query, QueryFont}; use swash::shape::*; -#[cfg(feature = "std")] use swash::text::cluster::{CharCluster, CharInfo, Token}; use swash::text::{Language, Script}; use swash::{FontRef, Synthesis}; -#[cfg(feature = "std")] +use alloc::vec::Vec; + use crate::inline_box::InlineBox; struct Item { @@ -33,7 +29,6 @@ struct Item { letter_spacing: f32, } -#[cfg(feature = "std")] #[allow(clippy::too_many_arguments)] pub fn shape_text<'a, B: Brush>( rcx: &'a ResolveContext, @@ -265,7 +260,6 @@ impl<'a, 'b, B: Brush> FontSelector<'a, 'b, B> { } } -#[cfg(feature = "std")] impl partition::Selector for FontSelector<'_, '_, B> { type SelectedFont = SelectedFont; diff --git a/parley/src/style/font.rs b/parley/src/style/font.rs index 18181aae..19fd3ad6 100644 --- a/parley/src/style/font.rs +++ b/parley/src/style/font.rs @@ -44,9 +44,10 @@ impl<'a> FontFamily<'a> { /// /// # Example /// ``` + /// # extern crate alloc; + /// use alloc::borrow::Cow; /// use parley::style::FontFamily::{self, *}; /// use parley::style::GenericFamily::*; - /// use std::borrow::Cow; /// /// assert_eq!(FontFamily::parse("Palatino Linotype"), Some(Named(Cow::Borrowed("Palatino Linotype")))); /// assert_eq!(FontFamily::parse("monospace"), Some(Generic(Monospace))); @@ -63,9 +64,10 @@ impl<'a> FontFamily<'a> { /// /// # Example /// ``` + /// # extern crate alloc; + /// use alloc::borrow::Cow; /// use parley::style::FontFamily::{self, *}; /// use parley::style::GenericFamily::*; - /// use std::borrow::Cow; /// /// let source = "Arial, 'Times New Roman', serif"; /// diff --git a/parley/src/util.rs b/parley/src/util.rs index b23f3f74..01f44b92 100644 --- a/parley/src/util.rs +++ b/parley/src/util.rs @@ -3,6 +3,10 @@ //! Misc helpers. +#[cfg(feature = "libm")] +#[allow(unused_imports)] +use core_maths::*; + pub fn nearly_eq(x: f32, y: f32) -> bool { (x - y).abs() < f32::EPSILON }