Implement canvas text align

This commit is contained in:
Patrick Walton 2019-05-30 15:08:15 -07:00
parent 5974aeba7b
commit 607a518544
2 changed files with 75 additions and 38 deletions

View File

@ -20,10 +20,10 @@ use pathfinder_geometry::basic::transform2d::Transform2DF;
use pathfinder_geometry::color::ColorU;
use pathfinder_geometry::outline::{Contour, Outline};
use pathfinder_geometry::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle};
use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::scene::{PathObject, Scene};
use pathfinder_text::{SceneExt, TextRenderMode};
use skribo::{FontCollection, FontFamily, TextStyle};
use skribo::{FontCollection, FontFamily, Layout, TextStyle};
use std::default::Default;
use std::mem;
use std::sync::Arc;
@ -80,48 +80,49 @@ impl CanvasRenderingContext2D {
// Drawing text
pub fn fill_text(&mut self, string: &str, position: Point2DF) {
// TODO(pcwalton): Report errors.
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
let transform = Transform2DF::from_translation(position).post_mul(&self.current_state
.transform);
drop(self.scene.push_text(string,
&TextStyle { size: self.current_state.font_size },
&self.current_state.font_collection,
&transform,
TextRenderMode::Fill,
HintingOptions::None,
paint_id));
self.fill_or_stroke_text(string, position, paint_id, TextRenderMode::Fill);
}
pub fn stroke_text(&mut self, string: &str, position: Point2DF) {
// TODO(pcwalton): Report errors.
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
let render_mode = TextRenderMode::Stroke(self.current_state.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() }
}
fn fill_or_stroke_text(&mut self,
string: &str,
mut position: Point2DF,
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 = Transform2DF::from_translation(position).post_mul(&self.current_state
.transform);
drop(self.scene.push_text(string,
// TODO(pcwalton): Report errors.
drop(self.scene.push_layout(&layout,
&TextStyle { size: self.current_state.font_size },
&self.current_state.font_collection,
&transform,
TextRenderMode::Stroke(self.current_state.stroke_style),
render_mode,
HintingOptions::None,
paint_id));
}
pub fn measure_text(&self, string: &str) -> TextMetrics {
let layout = skribo::layout(&TextStyle { size: self.current_state.font_size },
fn layout_text(&self, string: &str) -> Layout {
skribo::layout(&TextStyle { size: self.current_state.font_size },
&self.current_state.font_collection,
string);
let width = match layout.glyphs.last() {
None => 0.0,
Some(last_glyph) => {
let glyph_id = last_glyph.glyph_id;
let font_metrics = last_glyph.font.font.metrics();
let glyph_rect = last_glyph.font.font.typographic_bounds(glyph_id).unwrap();
let scale_factor = layout.size / font_metrics.units_per_em as f32;
last_glyph.offset.x + glyph_rect.max_x() * scale_factor
}
};
TextMetrics { width }
string)
}
// Line styles
@ -158,6 +159,11 @@ impl CanvasRenderingContext2D {
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;
}
// Drawing paths
#[inline]
@ -236,6 +242,7 @@ pub struct State {
transform: Transform2DF,
font_collection: Arc<FontCollection>,
font_size: f32,
text_align: TextAlign,
fill_paint: Paint,
stroke_paint: Paint,
stroke_style: StrokeStyle,
@ -248,6 +255,7 @@ impl State {
transform: Transform2DF::default(),
font_collection: default_font_collection,
font_size: DEFAULT_FONT_SIZE,
text_align: TextAlign::Left,
fill_paint: Paint { color: ColorU::black() },
stroke_paint: Paint { color: ColorU::black() },
stroke_style: StrokeStyle::default(),
@ -340,6 +348,13 @@ impl FillStyle {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TextAlign {
Left,
Right,
Center,
}
// TODO(pcwalton): Support other fields.
#[derive(Clone, Copy, Debug)]
pub struct TextMetrics {
@ -373,3 +388,24 @@ impl CanvasFontContext {
}
}
}
// Text layout utilities
trait LayoutExt {
fn width(&self) -> f32;
}
impl LayoutExt for Layout {
fn width(&self) -> f32 {
let last_glyph = match self.glyphs.last() {
None => return 0.0,
Some(last_glyph) => last_glyph,
};
let glyph_id = last_glyph.glyph_id;
let font_metrics = last_glyph.font.font.metrics();
let glyph_rect = last_glyph.font.font.typographic_bounds(glyph_id).unwrap();
let scale_factor = self.size / font_metrics.units_per_em as f32;
last_glyph.offset.x + glyph_rect.max_x() * scale_factor
}
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D};
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, TextAlign};
use pathfinder_geometry::basic::point::{Point2DF, Point2DI};
use pathfinder_geometry::color::ColorF;
use pathfinder_gl::{GLDevice, GLVersion};
@ -58,7 +58,8 @@ fn main() {
// Draw the text.
canvas.set_font_size(32.0);
canvas.fill_text("Hello Pathfinder!", Point2DF::new(32.0, 48.0));
canvas.stroke_text("Goodbye Pathfinder!", Point2DF::new(32.0, 96.0));
canvas.set_text_align(TextAlign::Right);
canvas.stroke_text("Goodbye Pathfinder!", Point2DF::new(608.0, 464.0));
// Render the canvas to screen.
let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor);