Implement the missing pieces of `TextMetrics` for canvas.

This required a `font-kit` upgrade, and with it a `skribo` upgrade.
This commit is contained in:
Patrick Walton 2020-04-14 13:01:20 -07:00
parent b9b1472b6c
commit 5efdf2a04a
10 changed files with 239 additions and 163 deletions

68
Cargo.lock generated
View File

@ -286,7 +286,7 @@ name = "canvas_nanovg"
version = "0.1.0"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -307,7 +307,7 @@ dependencies = [
name = "canvas_text"
version = "0.1.0"
dependencies = [
"font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_canvas 0.1.0",
"pathfinder_color 0.1.0",
@ -700,12 +700,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dwrote"
version = "0.9.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -797,22 +798,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "font-kit"
version = "0.5.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"lyon_path 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_geometry 0.5.0",
"pathfinder_simd 0.5.0",
"servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1284,24 +1286,6 @@ dependencies = [
"pathfinder_lottie 0.1.0",
]
[[package]]
name = "lyon_geom"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lyon_path"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lyon_geom 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lzma-rs"
version = "0.1.2"
@ -1601,7 +1585,7 @@ name = "pathfinder_c"
version = "0.1.0"
dependencies = [
"cbindgen 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1622,13 +1606,13 @@ dependencies = [
name = "pathfinder_canvas"
version = "0.1.0"
dependencies = [
"font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_color 0.1.0",
"pathfinder_content 0.1.0",
"pathfinder_geometry 0.5.0",
"pathfinder_renderer 0.1.0",
"pathfinder_text 0.1.0",
"skribo 0.0.1 (git+https://github.com/pcwalton/skribo.git?rev=7386321f763cecbce9c4ece3401c81de62305036)",
"skribo 0.0.1 (git+https://github.com/pcwalton/skribo.git?rev=fe9c968521b4a6ed98c9166a6e620028fea285c1)",
]
[[package]]
@ -1842,12 +1826,11 @@ name = "pathfinder_text"
version = "0.1.0"
dependencies = [
"euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lyon_path 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_content 0.1.0",
"pathfinder_geometry 0.5.0",
"pathfinder_renderer 0.1.0",
"skribo 0.0.1 (git+https://github.com/pcwalton/skribo.git?rev=7386321f763cecbce9c4ece3401c81de62305036)",
"skribo 0.0.1 (git+https://github.com/pcwalton/skribo.git?rev=fe9c968521b4a6ed98c9166a6e620028fea285c1)",
]
[[package]]
@ -2295,13 +2278,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "skribo"
version = "0.0.1"
source = "git+https://github.com/pcwalton/skribo.git?rev=7386321f763cecbce9c4ece3401c81de62305036#7386321f763cecbce9c4ece3401c81de62305036"
source = "git+https://github.com/pcwalton/skribo.git?rev=fe9c968521b4a6ed98c9166a6e620028fea285c1#fe9c968521b4a6ed98c9166a6e620028fea285c1"
dependencies = [
"euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"harfbuzz-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_geometry 0.5.0",
"unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2805,6 +2788,14 @@ dependencies = [
"x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wio"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
@ -2913,7 +2904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum dispatch 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a"
"checksum downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6"
"checksum dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bd1369e02db5e9b842a9b67bce8a2fcc043beafb2ae8a799dd482d46ea1ff0d"
"checksum dwrote 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcdf488e3a52a7aa30a05732a3e58420e22acb4b2b75635a561fc6ffbcab59ef"
"checksum egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a373bc9844200b1ff15bd1b245931d1c20d09d06e4ec09f361171f29a4b0752d"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
@ -2925,7 +2916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f"
"checksum float-cmp 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e"
"checksum float-ord 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
"checksum font-kit 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "09b6e2b877097ffd0abac6597fea26fccb5ed7eb9da0a4094f11ccc8aba64efb"
"checksum font-kit 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f953474ebbe3460775ed2da52435477cc029493284d6ceb635598586a2c6298"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11926b2b410b469d0e9399eca4cbbe237a9ef02176c485803b29216307e8e028"
@ -2979,8 +2970,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9"
"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum lyon_geom 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca04310c9807612a311506106000b6eccb2e27bca9bfb594ce80fb8a31231f9d"
"checksum lyon_path 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bcb57ac24a5428539e2c7c0592766d5933c937d703f430990c669c00de96862"
"checksum lzma-rs 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0606857a51b9088eb75b52d8431b7b7c8656849cc6cb96dde9f3d18a1a4b58"
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
@ -3063,7 +3052,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
"checksum simplecss 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "596554e63596d556a0dbd681416342ca61c75f1a45203201e7e77d3fa2fa9014"
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
"checksum skribo 0.0.1 (git+https://github.com/pcwalton/skribo.git?rev=7386321f763cecbce9c4ece3401c81de62305036)" = "<none>"
"checksum skribo 0.0.1 (git+https://github.com/pcwalton/skribo.git?rev=fe9c968521b4a6ed98c9166a6e620028fea285c1)" = "<none>"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
"checksum smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93960e8975909fcb14cc755de93af2149d8b8f4eb368315537d40cfd0f324054"
@ -3119,6 +3108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum winit 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "65a5c1a5ef76ac31cc97ad29489acdbed2178f3fc12ca00ee6cb11d60adb5a3a"
"checksum wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"

View File

@ -64,3 +64,7 @@ default-members = [
"utils/svg-to-skia",
"utils/convert",
]
[patch.crates-io]
pathfinder_geometry = { path = "geometry" }
pathfinder_simd = { path = "simd" }

View File

@ -9,7 +9,7 @@ build = "build.rs"
crate-type = ["staticlib"]
[dependencies]
font-kit = "0.5"
font-kit = "0.6"
foreign-types = "0.3"
gl = "0.14"
libc = "0.2"

View File

@ -8,7 +8,7 @@ edition = "2018"
crate-type = ["rlib", "staticlib"]
[dependencies]
font-kit = { version = "0.5", optional = true }
font-kit = { version = "0.6", optional = true }
[dependencies.pathfinder_color]
path = "../color"
@ -28,7 +28,7 @@ optional = true
[dependencies.skribo]
git = "https://github.com/pcwalton/skribo.git"
rev = "7386321f763cecbce9c4ece3401c81de62305036"
rev = "fe9c968521b4a6ed98c9166a6e620028fea285c1"
optional = true
[features]

View File

@ -8,18 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{CanvasRenderingContext2D, TextAlign, TextBaseline};
use crate::{CanvasRenderingContext2D, State, TextAlign, TextBaseline};
use font_kit::canvas::RasterizationOptions;
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::metrics::Metrics;
use font_kit::properties::Properties;
use font_kit::source::{Source, SystemSource};
use font_kit::sources::mem::MemSource;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_renderer::paint::PaintId;
use pathfinder_text::{SceneExt, TextRenderMode};
use skribo::{FontCollection, FontFamily, FontRef, Layout, TextStyle};
@ -40,7 +40,9 @@ impl CanvasRenderingContext2D {
}
pub fn measure_text(&self, string: &str) -> TextMetrics {
TextMetrics { width: self.layout_text(string).width() }
let mut metrics = self.layout_text(string).metrics();
metrics.make_origin_relative(&self.current_state);
metrics
}
pub fn fill_layout(&mut self, layout: &Layout, transform: Transform2F) {
@ -69,25 +71,7 @@ impl CanvasRenderingContext2D {
let clip_path = self.current_state.clip_path;
let blend_mode = self.current_state.global_composite_operation.to_blend_mode();
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),
}
match self.current_state.text_baseline {
TextBaseline::Alphabetic => {}
TextBaseline::Top => position.set_y(position.y() + layout.ascent()),
TextBaseline::Middle => {
position.set_y(position.y() + util::lerp(layout.ascent(), layout.descent(), 0.5))
}
TextBaseline::Bottom => position.set_y(position.y() + layout.descent()),
TextBaseline::Ideographic => {
position.set_y(position.y() + layout.ideographic_baseline())
}
TextBaseline::Hanging => position.set_y(position.y() + layout.hanging_baseline()),
}
position += layout.metrics().text_origin(&self.current_state);
let transform = self.current_state.transform * Transform2F::from_translation(position);
// TODO(pcwalton): Report errors.
@ -151,10 +135,46 @@ impl CanvasRenderingContext2D {
}
}
// TODO(pcwalton): Support other fields.
/// Represents the dimensions of a piece of text in the canvas.
#[derive(Clone, Copy, Debug)]
pub struct TextMetrics {
/// The calculated width of a segment of inline text in pixels.
pub width: f32,
/// The distance from the alignment point given by the `text_align` state to the left side of
/// the bounding rectangle of the given text, in pixels. The distance is measured parallel to
/// the baseline.
pub actual_bounding_box_left: f32,
/// The distance from the alignment point given by the `text_align` state to the right side of
/// the bounding rectangle of the given text, in pixels. The distance is measured parallel to
/// the baseline.
pub actual_bounding_box_right: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the top of
/// the highest bounding rectangle of all the fonts used to render the text, in pixels.
pub font_bounding_box_ascent: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the bottom
/// of the highest bounding rectangle of all the fonts used to render the text, in pixels.
pub font_bounding_box_descent: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the top of
/// the bounding rectangle used to render the text, in pixels.
pub actual_bounding_box_ascent: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the bottom
/// of the bounding rectangle used to render the text, in pixels.
pub actual_bounding_box_descent: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the top of
/// the em square in the line box, in pixels.
pub em_height_ascent: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the bottom
/// of the em square in the line box, in pixels.
pub em_height_descent: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the hanging
/// baseline of the line box, in pixels.
pub hanging_baseline: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the
/// alphabetic baseline of the line box, in pixels.
pub alphabetic_baseline: f32,
/// The distance from the horizontal line indicated by the `text_baseline` state to the
/// ideographic baseline of the line box, in pixels.
pub ideographic_baseline: f32,
}
#[cfg(feature = "pf-text")]
@ -196,17 +216,108 @@ impl CanvasFontContext {
// Text layout utilities
impl TextMetrics {
fn text_origin(&self, state: &State) -> Vector2F {
let x = match state.text_align {
TextAlign::Left => 0.0,
TextAlign::Right => -self.width,
TextAlign::Center => -0.5 * self.width,
};
let y = match state.text_baseline {
TextBaseline::Alphabetic => 0.0,
TextBaseline::Top => self.em_height_ascent,
TextBaseline::Middle => util::lerp(self.em_height_ascent, self.em_height_descent, 0.5),
TextBaseline::Bottom => self.em_height_descent,
TextBaseline::Ideographic => self.ideographic_baseline,
TextBaseline::Hanging => self.hanging_baseline,
};
vec2f(x, y)
}
fn make_origin_relative(&mut self, state: &State) {
let text_origin = self.text_origin(state);
self.actual_bounding_box_left += text_origin.x();
self.actual_bounding_box_right += text_origin.x();
self.font_bounding_box_ascent -= text_origin.y();
self.font_bounding_box_descent -= text_origin.y();
self.actual_bounding_box_ascent -= text_origin.y();
self.actual_bounding_box_descent -= text_origin.y();
self.em_height_ascent -= text_origin.y();
self.em_height_descent -= text_origin.y();
self.hanging_baseline -= text_origin.y();
self.alphabetic_baseline -= text_origin.y();
self.ideographic_baseline -= text_origin.y();
}
}
pub trait LayoutExt {
fn metrics(&self) -> TextMetrics;
fn width(&self) -> f32;
fn fold_metric<G, F>(&self, get: G, fold: F) -> f32 where G: FnMut(&Metrics) -> f32,
F: FnMut(f32, f32) -> f32;
fn ascent(&self) -> f32;
fn descent(&self) -> f32;
fn actual_bounding_box_left(&self) -> f32;
fn actual_bounding_box_right(&self) -> f32;
fn hanging_baseline(&self) -> f32;
fn ideographic_baseline(&self) -> f32;
}
impl LayoutExt for Layout {
// NB: This does not return origin-relative values. To get those, call `make_origin_relative()`
// afterward.
fn metrics(&self) -> TextMetrics {
let (mut em_height_ascent, mut em_height_descent) = (0.0, 0.0);
let (mut font_bounding_box_ascent, mut font_bounding_box_descent) = (0.0, 0.0);
let (mut actual_bounding_box_ascent, mut actual_bounding_box_descent) = (0.0, 0.0);
let mut last_font: Option<Arc<Font>> = None;
for glyph in &self.glyphs {
match last_font {
Some(ref last_font) if Arc::ptr_eq(&last_font, &glyph.font.font) => {}
_ => {
let font = glyph.font.font.clone();
let font_metrics = font.metrics();
let scale_factor = self.size / font_metrics.units_per_em as f32;
em_height_ascent = (font_metrics.ascent * scale_factor).max(em_height_ascent);
em_height_descent =
(font_metrics.descent * scale_factor).min(em_height_descent);
font_bounding_box_ascent = (font_metrics.bounding_box.max_y() *
scale_factor).max(font_bounding_box_ascent);
font_bounding_box_descent = (font_metrics.bounding_box.min_y() *
scale_factor).min(font_bounding_box_descent);
last_font = Some(font);
}
}
let font = last_font.as_ref().unwrap();
let glyph_rect = font.raster_bounds(glyph.glyph_id,
self.size,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa).unwrap();
actual_bounding_box_ascent =
(glyph_rect.max_y() as f32).max(actual_bounding_box_ascent);
actual_bounding_box_descent =
(glyph_rect.min_y() as f32).min(actual_bounding_box_descent);
}
TextMetrics {
width: self.width(),
actual_bounding_box_left: self.actual_bounding_box_left(),
actual_bounding_box_right: self.actual_bounding_box_right(),
font_bounding_box_ascent,
font_bounding_box_descent,
actual_bounding_box_ascent,
actual_bounding_box_descent,
em_height_ascent,
em_height_descent,
alphabetic_baseline: 0.0,
hanging_baseline: self.hanging_baseline(),
ideographic_baseline: self.ideographic_baseline(),
}
}
fn width(&self) -> f32 {
let last_glyph = match self.glyphs.last() {
None => return 0.0,
@ -217,32 +328,41 @@ impl LayoutExt for Layout {
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
last_glyph.offset.x() + glyph_rect.max_x() * scale_factor
}
fn fold_metric<G, F>(&self, mut get: G, mut fold: F) -> f32 where G: FnMut(&Metrics) -> f32,
F: FnMut(f32, f32) -> f32 {
let (mut last_font_seen, mut value) = (None, 0.0);
for glyph in &self.glyphs {
if let Some(ref last_font_seen) = last_font_seen {
if Arc::ptr_eq(last_font_seen, &glyph.font.font) {
continue;
}
}
let font_metrics = glyph.font.font.metrics();
let scale_factor = self.size / font_metrics.units_per_em as f32;
value = fold(value, get(&font_metrics) * scale_factor);
last_font_seen = Some(glyph.font.font.clone());
}
value
fn actual_bounding_box_left(&self) -> f32 {
let first_glyph = match self.glyphs.get(0) {
None => return 0.0,
Some(first_glyph) => first_glyph,
};
let glyph_id = first_glyph.glyph_id;
let glyph_rect = first_glyph.font
.font
.raster_bounds(glyph_id,
self.size,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa).unwrap();
first_glyph.offset.x() + glyph_rect.min_x() as f32
}
fn ascent(&self) -> f32 {
self.fold_metric(|metrics| metrics.ascent, f32::max)
}
fn actual_bounding_box_right(&self) -> f32 {
let last_glyph = match self.glyphs.last() {
None => return 0.0,
Some(last_glyph) => last_glyph,
};
fn descent(&self) -> f32 {
self.fold_metric(|metrics| metrics.descent, f32::min)
let glyph_id = last_glyph.glyph_id;
let glyph_rect = last_glyph.font
.font
.raster_bounds(glyph_id,
self.size,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa).unwrap();
last_glyph.offset.x() + glyph_rect.max_x() as f32
}
fn hanging_baseline(&self) -> f32 {

View File

@ -6,7 +6,7 @@ edition = "2018"
[dependencies]
arrayvec = "0.5"
font-kit = "0.5"
font-kit = "0.6"
gl = "0.14"
sdl2 = "0.33"
sdl2-sys = "0.33"

View File

@ -235,6 +235,8 @@ fn draw_paragraph(context: &mut CanvasRenderingContext2D,
context.set_font(&[FONT_NAME_REGULAR, FONT_NAME_EMOJI][..]);
context.set_font_size(18.0);
context.set_fill_style(ColorU::white());
context.set_text_align(TextAlign::Left);
context.set_text_baseline(TextBaseline::Alphabetic);
let main_text = MultilineTextBox::new(context,
PARAGRAPH_TEXT,
origin + vec2f(0.0, 24.0),
@ -270,6 +272,8 @@ fn draw_paragraph(context: &mut CanvasRenderingContext2D,
// Fade out the tooltip when close to it.
context.set_font_size(11.0);
context.set_text_align(TextAlign::Left);
context.set_text_baseline(TextBaseline::Alphabetic);
let tooltip_origin = main_text.bounds.lower_left() + vec2f(0.0, 38.0);
let tooltip = MultilineTextBox::new(context, HOVER_TEXT, tooltip_origin, 150.0, 18.0);
let mouse_vector = mouse_position.clamp(tooltip.bounds.origin(),
@ -341,7 +345,7 @@ impl MultilineTextBox {
while let Some(mut paragraph) = text.pop_front() {
while !paragraph.is_empty() {
let mut line = Line::new(origin, max_width, line_height);
let mut line = Line::new(origin, max_width);
line.layout(context, &mut paragraph, space_width);
origin += vec2f(0.0, line_height);
@ -384,8 +388,8 @@ impl MultilineTextBox {
}
impl Line {
fn new(origin: Vector2F, max_width: f32, line_height: f32) -> Line {
Line { words: vec![], origin, ascent: line_height, descent: 0.0, width: 0.0, max_width }
fn new(origin: Vector2F, max_width: f32) -> Line {
Line { words: vec![], origin, ascent: 0.0, descent: 0.0, width: 0.0, max_width }
}
fn layout(&mut self,
@ -398,8 +402,8 @@ impl Line {
word_origin_x += space_width;
}
let word_width = context.measure_text(&word).width;
let new_line_width = word_origin_x + word_width;
let word_metrics = context.measure_text(&word);
let new_line_width = word_origin_x + word_metrics.width;
if self.width != 0.0 && new_line_width > self.max_width {
text.push_front(word);
return;
@ -407,6 +411,8 @@ impl Line {
self.words.push(Word { text: word, origin_x: word_origin_x });
self.width = new_line_width;
self.ascent = self.ascent.max(word_metrics.em_height_ascent);
self.descent = self.descent.min(word_metrics.em_height_descent);
}
}
@ -427,7 +433,7 @@ impl Line {
fn bounds(&self) -> RectF {
RectF::new(self.origin - vec2f(0.0, self.ascent),
vec2f(self.width, self.ascent + self.descent))
vec2f(self.width, self.ascent - self.descent))
}
fn hit_test(&self, context: &CanvasRenderingContext2D, mut mouse_position: Vector2F)

View File

@ -5,7 +5,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
[dependencies]
font-kit = "0.5"
font-kit = "0.6"
gl = "0.14"
sdl2 = "0.33"
sdl2-sys = "0.33"

View File

@ -6,8 +6,7 @@ edition = "2018"
[dependencies]
euclid = "0.20"
font-kit = "0.5"
lyon_path = "0.14"
font-kit = "0.6"
[dependencies.pathfinder_content]
path = "../content"
@ -20,4 +19,4 @@ path = "../renderer"
[dependencies.skribo]
git = "https://github.com/pcwalton/skribo.git"
rev = "7386321f763cecbce9c4ece3401c81de62305036"
rev = "fe9c968521b4a6ed98c9166a6e620028fea285c1"

View File

@ -8,15 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use euclid::Angle;
use euclid::default::{Point2D, Vector2D};
use font_kit::error::GlyphLoadingError;
use font_kit::hinting::HintingOptions;
use font_kit::loader::Loader;
use lyon_path::builder::{FlatPathBuilder, PathBuilder, Build};
use font_kit::outline::OutlineSink;
use pathfinder_content::effects::BlendMode;
use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_renderer::paint::PaintId;
@ -105,7 +104,7 @@ impl SceneExt for Scene {
paint_id: PaintId)
-> Result<(), GlyphLoadingError> {
for glyph in &layout.glyphs {
let offset = Vector2F::new(glyph.offset.x, glyph.offset.y);
let offset = glyph.offset;
let font = &*glyph.font.font;
// FIXME(pcwalton): Cache this!
let scale = style.size / (font.metrics().units_per_em as f32);
@ -174,75 +173,33 @@ impl OutlinePathBuilder {
}
}
fn convert_point(&self, point: Point2D<f32>) -> Vector2F {
self.transform * Vector2F::new(point.x, point.y)
}
}
impl PathBuilder for OutlinePathBuilder {
fn quadratic_bezier_to(&mut self, ctrl: Point2D<f32>, to: Point2D<f32>) {
let (ctrl, to) = (self.convert_point(ctrl), self.convert_point(to));
self.current_contour.push_quadratic(ctrl, to);
}
fn cubic_bezier_to(&mut self, ctrl0: Point2D<f32>, ctrl1: Point2D<f32>, to: Point2D<f32>) {
let (ctrl0, ctrl1) = (self.convert_point(ctrl0), self.convert_point(ctrl1));
let to = self.convert_point(to);
self.current_contour.push_cubic(ctrl0, ctrl1, to);
}
fn arc(&mut self,
_center: Point2D<f32>,
_radii: Vector2D<f32>,
_sweep_angle: Angle<f32>,
_x_rotation: Angle<f32>) {
// TODO(pcwalton): Arcs.
}
}
impl Build for OutlinePathBuilder {
type PathType = Outline;
fn build(mut self) -> Outline {
self.flush_current_contour();
self.outline
}
fn build_and_reset(&mut self) -> Outline {
self.flush_current_contour();
mem::replace(&mut self.outline, Outline::new())
}
}
impl FlatPathBuilder for OutlinePathBuilder {
fn move_to(&mut self, to: Point2D<f32>) {
impl OutlineSink for OutlinePathBuilder {
fn move_to(&mut self, to: Vector2F) {
self.flush_current_contour();
let to = self.convert_point(to);
self.current_contour.push_endpoint(to);
self.current_contour.push_endpoint(self.transform * to);
}
fn line_to(&mut self, to: Point2D<f32>) {
let to = self.convert_point(to);
self.current_contour.push_endpoint(to);
fn line_to(&mut self, to: Vector2F) {
self.current_contour.push_endpoint(self.transform * to);
}
fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
self.current_contour.push_quadratic(self.transform * ctrl, self.transform * to);
}
fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
self.current_contour.push_cubic(self.transform * ctrl.from(),
self.transform * ctrl.to(),
self.transform * to);
}
fn close(&mut self) {
self.current_contour.close();
}
fn current_position(&self) -> Point2D<f32> {
if self.current_contour.is_empty() {
return Point2D::new(0.0, 0.0)
}
let point_index = if self.current_contour.is_closed() {
0
} else {
self.current_contour.len() - 1
};
let point = self.current_contour.position_of(point_index);
Point2D::new(point.x(), point.y())
}
}