Make text rendering optional in pathfinder_canvas

This commit is contained in:
est31 2019-07-27 06:02:20 +02:00
parent 3c18f99bf8
commit fa1a32bdbb
6 changed files with 154 additions and 115 deletions

View File

@ -15,6 +15,7 @@ gl = "0.6"
libc = "0.2" libc = "0.2"
[dependencies.pathfinder_canvas] [dependencies.pathfinder_canvas]
features = ["text"]
path = "../canvas" path = "../canvas"
[dependencies.pathfinder_content] [dependencies.pathfinder_content]

View File

@ -8,7 +8,7 @@ edition = "2018"
crate-type = ["rlib", "staticlib"] crate-type = ["rlib", "staticlib"]
[dependencies] [dependencies]
font-kit = "0.2" font-kit = { version = "0.2", optional = true }
[dependencies.pathfinder_content] [dependencies.pathfinder_content]
path = "../content" path = "../content"
@ -21,7 +21,12 @@ path = "../renderer"
[dependencies.pathfinder_text] [dependencies.pathfinder_text]
path = "../text" path = "../text"
optional = true
[dependencies.skribo] [dependencies.skribo]
git = "https://github.com/linebender/skribo.git" git = "https://github.com/linebender/skribo.git"
rev = "a2d683856ba1f2d0095b12dd7823d1602a87614e" rev = "a2d683856ba1f2d0095b12dd7823d1602a87614e"
optional = true
[features]
text = ["pathfinder_text", "skribo", "font-kit"]

View File

@ -10,13 +10,6 @@
//! A simple API for Pathfinder that mirrors a subset of HTML canvas. //! A simple API for Pathfinder that mirrors a subset of HTML canvas.
use font_kit::family_name::FamilyName;
use font_kit::handle::Handle;
use font_kit::hinting::HintingOptions;
use font_kit::loaders::default::Font;
use font_kit::properties::Properties;
use font_kit::source::{Source, SystemSource};
use font_kit::sources::mem::MemSource;
use pathfinder_content::color::ColorU; use pathfinder_content::color::ColorU;
use pathfinder_content::dash::OutlineDash; use pathfinder_content::dash::OutlineDash;
use pathfinder_content::outline::{ArcDirection, Contour, Outline}; use pathfinder_content::outline::{ArcDirection, Contour, Outline};
@ -28,17 +21,33 @@ use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::scene::{PathObject, Scene}; use pathfinder_renderer::scene::{PathObject, Scene};
use pathfinder_text::{SceneExt, TextRenderMode};
use skribo::{FontCollection, FontFamily, Layout, TextStyle};
use std::default::Default; use std::default::Default;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::iter;
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "text")]
use text_imports::*;
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333; const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
const DEFAULT_FONT_SIZE: f32 = 10.0; const DEFAULT_FONT_SIZE: f32 = 10.0;
#[cfg(feature = "text")]
mod text_imports {
pub use std::iter;
pub use font_kit::family_name::FamilyName;
pub use font_kit::handle::Handle;
pub use font_kit::hinting::HintingOptions;
pub use font_kit::loaders::default::Font;
pub use font_kit::properties::Properties;
pub use font_kit::source::{Source, SystemSource};
pub use font_kit::sources::mem::MemSource;
pub use skribo::{FontCollection, FontFamily, Layout, TextStyle};
pub use pathfinder_text::{SceneExt, TextRenderMode};
}
#[cfg(not(feature = "text"))]
pub struct FontCollection;
pub struct CanvasRenderingContext2D { pub struct CanvasRenderingContext2D {
scene: Scene, scene: Scene,
current_state: State, current_state: State,
@ -56,9 +65,13 @@ impl CanvasRenderingContext2D {
} }
pub fn from_scene(font_context: CanvasFontContext, scene: Scene) -> CanvasRenderingContext2D { pub fn from_scene(font_context: CanvasFontContext, scene: Scene) -> CanvasRenderingContext2D {
#[cfg(feature = "text")]
let default_font_collection = font_context.default_font_collection.clone();
#[cfg(not(feature = "text"))]
let default_font_collection = Arc::new(FontCollection);
CanvasRenderingContext2D { CanvasRenderingContext2D {
scene, scene,
current_state: State::default(font_context.default_font_collection.clone()), current_state: State::default(default_font_collection),
saved_states: vec![], saved_states: vec![],
font_context, font_context,
} }
@ -85,63 +98,6 @@ impl CanvasRenderingContext2D {
self.stroke_path(path); self.stroke_path(path);
} }
// Drawing text
pub fn fill_text(&mut self, string: &str, position: Vector2F) {
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
self.fill_or_stroke_text(string, position, paint_id, TextRenderMode::Fill);
}
pub fn stroke_text(&mut self, string: &str, position: Vector2F) {
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style());
self.fill_or_stroke_text(string, position, paint_id, render_mode);
}
pub fn measure_text(&self, string: &str) -> TextMetrics {
TextMetrics { width: self.layout_text(string).width() }
}
pub fn fill_layout(&mut self, layout: &Layout, transform: Transform2F) {
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
drop(self.scene.push_layout(&layout,
&TextStyle { size: self.current_state.font_size },
&(transform * self.current_state.transform),
TextRenderMode::Fill,
HintingOptions::None,
paint_id));
}
fn fill_or_stroke_text(&mut self,
string: &str,
mut position: Vector2F,
paint_id: PaintId,
render_mode: TextRenderMode) {
let layout = self.layout_text(string);
match self.current_state.text_align {
TextAlign::Left => {},
TextAlign::Right => position.set_x(position.x() - layout.width()),
TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5),
}
let transform = self.current_state.transform * Transform2F::from_translation(position);
// TODO(pcwalton): Report errors.
drop(self.scene.push_layout(&layout,
&TextStyle { size: self.current_state.font_size },
&transform,
render_mode,
HintingOptions::None,
paint_id));
}
fn layout_text(&self, string: &str) -> Layout {
skribo::layout(&TextStyle { size: self.current_state.font_size },
&self.current_state.font_collection,
string)
}
// Line styles // Line styles
#[inline] #[inline]
@ -181,52 +137,6 @@ impl CanvasRenderingContext2D {
self.current_state.line_dash_offset = new_line_dash_offset self.current_state.line_dash_offset = new_line_dash_offset
} }
// Text styles
#[inline]
pub fn set_font_collection(&mut self, font_collection: Arc<FontCollection>) {
self.current_state.font_collection = font_collection;
}
#[inline]
pub fn set_font_families<I>(&mut self, font_families: I) where I: Iterator<Item = FontFamily> {
let mut font_collection = FontCollection::new();
for font_family in font_families {
font_collection.add_family(font_family);
}
self.current_state.font_collection = Arc::new(font_collection);
}
/// A convenience method to set a single font family.
#[inline]
pub fn set_font_family(&mut self, font_family: FontFamily) {
self.set_font_families(iter::once(font_family))
}
/// A convenience method to set a single font family consisting of a single font.
#[inline]
pub fn set_font(&mut self, font: Font) {
self.set_font_family(FontFamily::new_from_font(font))
}
/// A convenience method to set a single font family consisting of a font
/// described by a PostScript name.
#[inline]
pub fn set_font_by_postscript_name(&mut self, postscript_name: &str) {
let font = self.font_context.font_source.select_by_postscript_name(postscript_name);
self.set_font(font.expect("Didn't find the font!").load().unwrap());
}
#[inline]
pub fn set_font_size(&mut self, new_font_size: f32) {
self.current_state.font_size = new_font_size;
}
#[inline]
pub fn set_text_align(&mut self, new_text_align: TextAlign) {
self.current_state.text_align = new_text_align;
}
// Fill and stroke styles // Fill and stroke styles
#[inline] #[inline]
@ -346,6 +256,111 @@ impl CanvasRenderingContext2D {
} }
} }
// Drawing text
#[cfg(feature = "text")]
impl CanvasRenderingContext2D {
pub fn fill_text(&mut self, string: &str, position: Vector2F) {
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
self.fill_or_stroke_text(string, position, paint_id, TextRenderMode::Fill);
}
pub fn stroke_text(&mut self, string: &str, position: Vector2F) {
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style());
self.fill_or_stroke_text(string, position, paint_id, render_mode);
}
pub fn measure_text(&self, string: &str) -> TextMetrics {
TextMetrics { width: self.layout_text(string).width() }
}
pub fn fill_layout(&mut self, layout: &Layout, transform: Transform2F) {
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
drop(self.scene.push_layout(&layout,
&TextStyle { size: self.current_state.font_size },
&(transform * self.current_state.transform),
TextRenderMode::Fill,
HintingOptions::None,
paint_id));
}
fn fill_or_stroke_text(&mut self,
string: &str,
mut position: Vector2F,
paint_id: PaintId,
render_mode: TextRenderMode) {
let layout = self.layout_text(string);
match self.current_state.text_align {
TextAlign::Left => {},
TextAlign::Right => position.set_x(position.x() - layout.width()),
TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5),
}
let transform = self.current_state.transform * Transform2F::from_translation(position);
// TODO(pcwalton): Report errors.
drop(self.scene.push_layout(&layout,
&TextStyle { size: self.current_state.font_size },
&transform,
render_mode,
HintingOptions::None,
paint_id));
}
fn layout_text(&self, string: &str) -> Layout {
skribo::layout(&TextStyle { size: self.current_state.font_size },
&self.current_state.font_collection,
string)
}
// Text styles
#[inline]
pub fn set_font_collection(&mut self, font_collection: Arc<FontCollection>) {
self.current_state.font_collection = font_collection;
}
#[inline]
pub fn set_font_families<I>(&mut self, font_families: I) where I: Iterator<Item = FontFamily> {
let mut font_collection = FontCollection::new();
for font_family in font_families {
font_collection.add_family(font_family);
}
self.current_state.font_collection = Arc::new(font_collection);
}
/// A convenience method to set a single font family.
#[inline]
pub fn set_font_family(&mut self, font_family: FontFamily) {
self.set_font_families(iter::once(font_family))
}
/// A convenience method to set a single font family consisting of a single font.
#[inline]
pub fn set_font(&mut self, font: Font) {
self.set_font_family(FontFamily::new_from_font(font))
}
/// A convenience method to set a single font family consisting of a font
/// described by a PostScript name.
#[inline]
pub fn set_font_by_postscript_name(&mut self, postscript_name: &str) {
let font = self.font_context.font_source.select_by_postscript_name(postscript_name);
self.set_font(font.expect("Didn't find the font!").load().unwrap());
}
#[inline]
pub fn set_font_size(&mut self, new_font_size: f32) {
self.current_state.font_size = new_font_size;
}
#[inline]
pub fn set_text_align(&mut self, new_text_align: TextAlign) {
self.current_state.text_align = new_text_align;
}
}
#[derive(Clone)] #[derive(Clone)]
struct State { struct State {
transform: Transform2F, transform: Transform2F,
@ -544,12 +559,14 @@ pub enum LineJoin {
} }
// TODO(pcwalton): Support other fields. // TODO(pcwalton): Support other fields.
#[cfg(feature = "text")]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct TextMetrics { pub struct TextMetrics {
pub width: f32, pub width: f32,
} }
#[derive(Clone)] #[derive(Clone)]
#[cfg(feature = "text")]
pub struct CanvasFontContext { pub struct CanvasFontContext {
#[allow(dead_code)] #[allow(dead_code)]
font_source: Arc<dyn Source>, font_source: Arc<dyn Source>,
@ -557,6 +574,7 @@ pub struct CanvasFontContext {
default_font_collection: Arc<FontCollection>, default_font_collection: Arc<FontCollection>,
} }
#[cfg(feature = "text")]
impl CanvasFontContext { impl CanvasFontContext {
pub fn new(font_source: Arc<dyn Source>) -> CanvasFontContext { pub fn new(font_source: Arc<dyn Source>) -> CanvasFontContext {
let mut default_font_collection = FontCollection::new(); let mut default_font_collection = FontCollection::new();
@ -585,12 +603,24 @@ impl CanvasFontContext {
} }
} }
#[cfg(not(feature = "text"))]
pub struct CanvasFontContext;
#[cfg(not(feature = "text"))]
impl CanvasFontContext {
pub fn from_system_source() -> Self {
CanvasFontContext
}
}
// Text layout utilities // Text layout utilities
#[cfg(feature = "text")]
trait LayoutExt { trait LayoutExt {
fn width(&self) -> f32; fn width(&self) -> f32;
} }
#[cfg(feature = "text")]
impl LayoutExt for Layout { impl LayoutExt for Layout {
fn width(&self) -> f32 { fn width(&self) -> f32 {
let last_glyph = match self.glyphs.last() { let last_glyph = match self.glyphs.last() {

View File

@ -13,6 +13,7 @@ sdl2 = "0.32"
sdl2-sys = "0.32" sdl2-sys = "0.32"
[dependencies.pathfinder_canvas] [dependencies.pathfinder_canvas]
features = ["text"]
path = "../../canvas" path = "../../canvas"
[dependencies.pathfinder_content] [dependencies.pathfinder_content]

View File

@ -10,6 +10,7 @@ sdl2 = "0.32"
sdl2-sys = "0.32" sdl2-sys = "0.32"
[dependencies.pathfinder_canvas] [dependencies.pathfinder_canvas]
features = ["text"]
path = "../../canvas" path = "../../canvas"
[dependencies.pathfinder_content] [dependencies.pathfinder_content]

View File

@ -11,6 +11,7 @@ sdl2 = "0.32"
sdl2-sys = "0.32" sdl2-sys = "0.32"
[dependencies.pathfinder_canvas] [dependencies.pathfinder_canvas]
features = ["text"]
path = "../../canvas" path = "../../canvas"
[dependencies.pathfinder_content] [dependencies.pathfinder_content]