From 268805ba0cc16d22321d129b96b23678963be513 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 8 Nov 2022 08:43:27 -0700 Subject: [PATCH] no_std support --- Cargo.toml | 25 +++++++-- examples/terminal/src/main.rs | 6 ++- src/attrs.rs | 7 ++- src/buffer.rs | 40 ++++++++------ src/buffer_line.rs | 6 +++ src/cache.rs | 8 +-- src/editor.rs | 9 +++- src/font/fallback/mod.rs | 4 +- src/font/font.rs | 4 +- src/font/matches.rs | 7 ++- src/font/system/mod.rs | 9 ++++ src/font/system/no_std.rs | 75 +++++++++++++++++++++++++++ src/font/{system.rs => system/std.rs} | 0 src/layout.rs | 3 ++ src/lib.rs | 4 ++ src/shape.rs | 9 ++-- src/swash.rs | 9 ++-- test.sh | 3 ++ 18 files changed, 190 insertions(+), 38 deletions(-) create mode 100644 src/font/system/mod.rs create mode 100644 src/font/system/no_std.rs rename src/font/{system.rs => system/std.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 6957b9c..77dc65a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,19 +9,34 @@ documentation = "https://docs.rs/cosmic-text/latest/cosmic_text/" repository = "https://github.com/pop-os/cosmic-text" [dependencies] -fontdb = "0.9.3" +fontdb = { version = "0.10", default-features = false } +libm = "0.2" log = "0.4" ouroboros = "0.15.5" -rustybuzz = "0.5" +rustybuzz = { version = "0.5", default-features = false, features = ["libm"]} swash = { version = "0.1", optional = true } -sys-locale = "0.2" -unicode-bidi = "0.3" +sys-locale = { version = "0.2", optional = true } unicode-linebreak = "0.1" unicode-script = "0.5" unicode-segmentation = "1.7" +[dependencies.unicode-bidi] +version = "0.3" +default-features = false +features = ["hardcoded-data"] + [features] -default = ["swash"] +default = ["std", "swash"] +no_std = [ + "rustybuzz/libm", +] +std = [ + "fontdb/memmap", + "fontdb/std", + "rustybuzz/std", + "sys-locale", + "unicode-bidi/std", +] [workspace] members = [ diff --git a/examples/terminal/src/main.rs b/examples/terminal/src/main.rs index 5bba79f..4c121e8 100644 --- a/examples/terminal/src/main.rs +++ b/examples/terminal/src/main.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + use cosmic_text::{Attrs, Color, FontSystem, SwashCache, Buffer, Metrics}; use std::cmp; use termion::{ @@ -92,9 +94,9 @@ fn main() { print!( "{} {}", color::Bg(color::Rgb( - scale(color.b()), - scale(color.g()), scale(color.r()), + scale(color.g()), + scale(color.b()), )), color::Bg(color::Reset), ); diff --git a/src/attrs.rs b/src/attrs.rs index 0282a7e..ba71861 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -1,6 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::ops::Range; +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use core::ops::Range; pub use fontdb::{Family, Stretch, Style, Weight}; diff --git a/src/buffer.rs b/src/buffer.rs index 4ca49f0..2ee819a 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,13 +1,19 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::{ +#[cfg(not(feature = "std"))] +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use core::{ cmp, fmt, - time::Instant, }; use unicode_segmentation::UnicodeSegmentation; -use crate::{Attrs, AttrsList, BufferLine, Color, FontSystem, LayoutGlyph, LayoutLine, ShapeLine}; +use crate::{Attrs, AttrsList, BufferLine, FontSystem, LayoutGlyph, LayoutLine, ShapeLine}; +#[cfg(feature = "swash")] +use crate::Color; /// Current cursor location #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -170,7 +176,8 @@ impl<'a> Buffer<'a> { } fn relayout(&mut self) { - let instant = Instant::now(); + #[cfg(feature = "std")] + let instant = std::time::Instant::now(); for line in self.lines.iter_mut() { if line.shape_opt().is_some() { @@ -185,13 +192,14 @@ impl<'a> Buffer<'a> { self.redraw = true; - let duration = instant.elapsed(); - log::debug!("relayout: {:?}", duration); + #[cfg(feature = "std")] + log::debug!("relayout: {:?}", instant.elapsed()); } /// Pre-shape lines in the buffer, up to `lines`, return actual number of layout lines pub fn shape_until(&mut self, lines: i32) -> i32 { - let instant = Instant::now(); + #[cfg(feature = "std")] + let instant = std::time::Instant::now(); let mut reshaped = 0; let mut total_layout = 0; @@ -211,9 +219,9 @@ impl<'a> Buffer<'a> { total_layout += layout.len() as i32; } - let duration = instant.elapsed(); if reshaped > 0 { - log::debug!("shape_until {}: {:?}", reshaped, duration); + #[cfg(feature = "std")] + log::debug!("shape_until {}: {:?}", reshaped, instant.elapsed()); self.redraw = true; } @@ -222,7 +230,8 @@ impl<'a> Buffer<'a> { /// Shape lines until cursor, also scrolling to include cursor in view pub fn shape_until_cursor(&mut self, cursor: Cursor) { - let instant = Instant::now(); + #[cfg(feature = "std")] + let instant = std::time::Instant::now(); let mut reshaped = 0; let mut layout_i = 0; @@ -248,9 +257,9 @@ impl<'a> Buffer<'a> { } } - let duration = instant.elapsed(); if reshaped > 0 { - log::debug!("shape_until_cursor {}: {:?}", reshaped, duration); + #[cfg(feature = "std")] + log::debug!("shape_until_cursor {}: {:?}", reshaped, instant.elapsed()); self.redraw = true; } @@ -409,7 +418,8 @@ impl<'a> Buffer<'a> { /// Convert x, y position to Cursor (hit detection) pub fn hit(&self, x: i32, y: i32) -> Option { - let instant = Instant::now(); + #[cfg(feature = "std")] + let instant = std::time::Instant::now(); let font_size = self.metrics.font_size; let line_height = self.metrics.line_height; @@ -485,8 +495,8 @@ impl<'a> Buffer<'a> { } } - let duration = instant.elapsed(); - log::trace!("click({}, {}): {:?}", x, y, duration); + #[cfg(feature = "std")] + log::trace!("click({}, {}): {:?}", x, y, instant.elapsed()); new_cursor_opt } diff --git a/src/buffer_line.rs b/src/buffer_line.rs index a82c86c..21d4a06 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -1,3 +1,9 @@ +#[cfg(not(feature = "std"))] +use alloc::{ + string::String, + vec::Vec, +}; + use crate::{AttrsList, FontSystem, LayoutLine, ShapeLine}; /// A line (or paragraph) of text that is shaped and laid out diff --git a/src/cache.rs b/src/cache.rs index 9c77626..4859e3e 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 /// Key for building a glyph cache -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct CacheKey { /// Font ID pub font_id: fontdb::ID, @@ -39,7 +39,7 @@ impl CacheKey { } /// Binning of subpixel position for cache optimization -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum SubpixelBin { Zero, One, @@ -49,8 +49,8 @@ pub enum SubpixelBin { impl SubpixelBin { pub fn new(pos: f32) -> (i32, Self) { - let trunc = pos.trunc() as i32; - let fract = pos.fract(); + let (fract, truncf) = libm::modff(pos); + let trunc = truncf as i32; if pos.is_sign_negative() { if fract > -0.125 { (trunc, Self::Zero) diff --git a/src/editor.rs b/src/editor.rs index 6bb6dc9..eb37e33 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,9 +1,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::cmp; +#[cfg(not(feature = "std"))] +use alloc::string::ToString; +#[cfg(feature = "swash")] +use core::cmp; use unicode_segmentation::UnicodeSegmentation; -use crate::{AttrsList, Buffer, BufferLine, Color, Cursor, LayoutCursor}; +use crate::{AttrsList, Buffer, BufferLine, Cursor, LayoutCursor}; +#[cfg(feature = "swash")] +use crate::Color; /// An action to perform on an [Editor] #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index d3b6c1a..8a7d0a6 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::sync::Arc; +use alloc::sync::Arc; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use unicode_script::Script; use crate::Font; diff --git a/src/font/font.rs b/src/font/font.rs index 5a6c77a..abb9d2b 100644 --- a/src/font/font.rs +++ b/src/font/font.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::ops::Deref; +use core::ops::Deref; pub struct Font<'a> { pub info: &'a fontdb::FaceInfo, @@ -14,10 +14,12 @@ impl<'a> Font<'a> { pub fn new(info: &'a fontdb::FaceInfo) -> Option { let data = match &info.source { fontdb::Source::Binary(data) => data.deref().as_ref(), + #[cfg(feature = "std")] fontdb::Source::File(path) => { log::warn!("Unsupported fontdb Source::File('{}')", path.display()); return None; } + #[cfg(feature = "std")] fontdb::Source::SharedFile(_path, data) => data.deref().as_ref(), }; diff --git a/src/font/matches.rs b/src/font/matches.rs index dc2b3c3..03376c0 100644 --- a/src/font/matches.rs +++ b/src/font/matches.rs @@ -1,6 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::sync::Arc; +use alloc::sync::Arc; +#[cfg(not(feature = "std"))] +use alloc::{ + string::String, + vec::Vec, +}; use crate::Font; diff --git a/src/font/system/mod.rs b/src/font/system/mod.rs new file mode 100644 index 0000000..636398a --- /dev/null +++ b/src/font/system/mod.rs @@ -0,0 +1,9 @@ +#[cfg(not(feature = "std"))] +pub use self::no_std::*; +#[cfg(not(feature = "std"))] +mod no_std; + +#[cfg(feature = "std")] +pub use self::std::*; +#[cfg(feature = "std")] +mod std; diff --git a/src/font/system/no_std.rs b/src/font/system/no_std.rs new file mode 100644 index 0000000..d18d3dc --- /dev/null +++ b/src/font/system/no_std.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use alloc::{ + string::{String, ToString}, + sync::Arc, + vec::Vec, +}; + +use crate::{Attrs, Font, FontMatches}; + +/// Access system fonts +pub struct FontSystem{ + locale: String, + db: fontdb::Database, +} + +impl FontSystem { + pub fn new() -> Self { + //TODO: get locale from argument? + let locale = "en-US".to_string(); + + //TODO: allow loading fonts from memory + let mut db = fontdb::Database::new(); + { + //TODO: configurable default fonts + db.set_monospace_family("Fira Mono"); + db.set_sans_serif_family("Fira Sans"); + db.set_serif_family("DejaVu Serif"); + } + + Self { + locale, + db, + } + } + + pub fn locale(&self) -> &str { + &self.locale + } + + pub fn db(&self) -> &fontdb::Database { + &self.db + } + + pub fn get_font<'a>(&'a self, id: fontdb::ID) -> Option>> { + let face = self.db.face(id)?; + match Font::new(face) { + Some(font) => Some(Arc::new(font)), + None => { + log::warn!("failed to load font '{}'", face.post_script_name); + None + } + } + } + + pub fn get_font_matches<'a>(&'a self, attrs: Attrs) -> Arc> { + let mut fonts = Vec::new(); + for face in self.db.faces() { + if !attrs.matches(face) { + continue; + } + + match self.get_font(face.id) { + Some(font) => fonts.push(font), + None => (), + } + } + + Arc::new(FontMatches { + locale: &self.locale, + default_family: self.db.family_name(&attrs.family).to_string(), + fonts + }) + } +} diff --git a/src/font/system.rs b/src/font/system/std.rs similarity index 100% rename from src/font/system.rs rename to src/font/system/std.rs diff --git a/src/layout.rs b/src/layout.rs index 1ddf240..2bc1b1b 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,5 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + use crate::{CacheKey, Color}; /// A laid out glyph diff --git a/src/lib.rs b/src/lib.rs index 84997d1..e5053f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,10 @@ //! }); //! ``` +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + pub use self::attrs::*; mod attrs; diff --git a/src/shape.rs b/src/shape.rs index 9f3757f..87c5c0d 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -1,5 +1,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +use core::mem; use unicode_script::{Script, UnicodeScript}; use unicode_segmentation::UnicodeSegmentation; @@ -592,7 +595,7 @@ impl ShapeLine { }; if word_wrap && !wrap_simple && !glyphs.is_empty() { let mut glyphs_swap = Vec::new(); - std::mem::swap(&mut glyphs, &mut glyphs_swap); + mem::swap(&mut glyphs, &mut glyphs_swap); layout_lines.push( LayoutLine { glyphs: glyphs_swap, @@ -616,7 +619,7 @@ impl ShapeLine { if glyph_wrap { let mut glyphs_swap = Vec::new(); - std::mem::swap(&mut glyphs, &mut glyphs_swap); + mem::swap(&mut glyphs, &mut glyphs_swap); layout_lines.push( LayoutLine { glyphs: glyphs_swap, @@ -643,7 +646,7 @@ impl ShapeLine { if wrap { let mut glyphs_swap = Vec::new(); - std::mem::swap(&mut glyphs, &mut glyphs_swap); + mem::swap(&mut glyphs, &mut glyphs_swap); layout_lines.push( LayoutLine { glyphs: glyphs_swap, diff --git a/src/swash.rs b/src/swash.rs index 70a68db..9e76563 100644 --- a/src/swash.rs +++ b/src/swash.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::collections::HashMap; +#[cfg(not(feature = "std"))] +use alloc::collections::BTreeMap as Map; +#[cfg(feature = "std")] +use std::collections::HashMap as Map; use swash::scale::{ScaleContext, image::Content}; use swash::scale::{Render, Source, StrikeWith}; use swash::zeno::{Format, Vector}; @@ -51,7 +54,7 @@ fn swash_image<'a>(font_system: &'a FontSystem, context: &mut ScaleContext, cach pub struct SwashCache<'a> { font_system: &'a FontSystem, context: ScaleContext, - pub image_cache: HashMap>, + pub image_cache: Map>, } impl<'a> SwashCache<'a> { @@ -60,7 +63,7 @@ impl<'a> SwashCache<'a> { Self { font_system: font_system, context: ScaleContext::new(), - image_cache: HashMap::new() + image_cache: Map::new() } } diff --git a/test.sh b/test.sh index 843f1f8..b680820 100755 --- a/test.sh +++ b/test.sh @@ -4,5 +4,8 @@ set -ex cargo doc cargo test +cargo build --release --no-default-features +cargo build --release --no-default-features --features std +cargo build --release --no-default-features --features swash cargo build --release --all env RUST_LOG=editor_test=info target/release/editor-test