mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Synchronous layout 🪀
This commit is contained in:
parent
e94627721d
commit
38157b0e0c
14
Cargo.toml
14
Cargo.toml
@ -7,11 +7,6 @@ edition = "2018"
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["main"]
|
members = ["main"]
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["serialize", "fs"]
|
|
||||||
serialize = ["serde"]
|
|
||||||
fs = ["fontdock/fs"]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
@ -21,19 +16,22 @@ opt-level = 2
|
|||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["fs"]
|
||||||
|
fs = ["fontdock/fs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1"
|
|
||||||
fontdock = { path = "../fontdock", default-features = false }
|
fontdock = { path = "../fontdock", default-features = false }
|
||||||
tide = { path = "../tide" }
|
tide = { path = "../tide" }
|
||||||
ttf-parser = "0.8.2"
|
ttf-parser = "0.8.2"
|
||||||
unicode-xid = "0.2"
|
unicode-xid = "0.2"
|
||||||
|
|
||||||
|
# feature = "serde"
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
futures-executor = "0.3"
|
|
||||||
kurbo = "0.6.3"
|
kurbo = "0.6.3"
|
||||||
serde_json = "1"
|
|
||||||
raqote = { version = "0.8", default-features = false }
|
raqote = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
|
@ -3,7 +3,6 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
use fontdock::fs::{FsIndex, FsSource};
|
use fontdock::fs::{FsIndex, FsSource};
|
||||||
use futures_executor::block_on;
|
|
||||||
|
|
||||||
use typstc::eval::{eval, State};
|
use typstc::eval::{eval, State};
|
||||||
use typstc::font::FontLoader;
|
use typstc::font::FontLoader;
|
||||||
@ -28,15 +27,15 @@ fn benchmarks(c: &mut Criterion) {
|
|||||||
|
|
||||||
let tree = parse(COMA).output;
|
let tree = parse(COMA).output;
|
||||||
let document = eval(&tree, state.clone()).output;
|
let document = eval(&tree, state.clone()).output;
|
||||||
let _ = block_on(layout(&document, Rc::clone(&loader)));
|
let _ = layout(&document, Rc::clone(&loader));
|
||||||
|
|
||||||
c.bench_function("parse-coma", |b| b.iter(|| parse(COMA)));
|
c.bench_function("parse-coma", |b| b.iter(|| parse(COMA)));
|
||||||
c.bench_function("eval-coma", |b| b.iter(|| eval(&tree, state.clone())));
|
c.bench_function("eval-coma", |b| b.iter(|| eval(&tree, state.clone())));
|
||||||
c.bench_function("layout-coma", |b| {
|
c.bench_function("layout-coma", |b| {
|
||||||
b.iter(|| block_on(layout(&document, Rc::clone(&loader))))
|
b.iter(|| layout(&document, Rc::clone(&loader)))
|
||||||
});
|
});
|
||||||
c.bench_function("typeset-coma", |b| {
|
c.bench_function("typeset-coma", |b| {
|
||||||
b.iter(|| block_on(typeset(COMA, state.clone(), Rc::clone(&loader))))
|
b.iter(|| typeset(COMA, state.clone(), Rc::clone(&loader)))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use fontdock::fs::{FsIndex, FsSource};
|
use fontdock::fs::{FsIndex, FsSource};
|
||||||
use futures_executor::block_on;
|
|
||||||
|
|
||||||
use typstc::diag::{Feedback, Pass};
|
use typstc::diag::{Feedback, Pass};
|
||||||
use typstc::eval::State;
|
use typstc::eval::State;
|
||||||
@ -48,7 +47,7 @@ fn main() {
|
|||||||
let Pass {
|
let Pass {
|
||||||
output: layouts,
|
output: layouts,
|
||||||
feedback: Feedback { mut diags, .. },
|
feedback: Feedback { mut diags, .. },
|
||||||
} = block_on(typeset(&src, state, Rc::clone(&loader)));
|
} = typeset(&src, state, Rc::clone(&loader));
|
||||||
|
|
||||||
if !diags.is_empty() {
|
if !diags.is_empty() {
|
||||||
diags.sort();
|
diags.sort();
|
||||||
|
10
src/diag.rs
10
src/diag.rs
@ -68,7 +68,7 @@ impl Feedback {
|
|||||||
|
|
||||||
/// A diagnostic that arose in parsing or layouting.
|
/// A diagnostic that arose in parsing or layouting.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
pub struct Diag {
|
pub struct Diag {
|
||||||
/// How severe / important the diagnostic is.
|
/// How severe / important the diagnostic is.
|
||||||
pub level: Level,
|
pub level: Level,
|
||||||
@ -78,8 +78,8 @@ pub struct Diag {
|
|||||||
|
|
||||||
/// How severe / important a diagnostic is.
|
/// How severe / important a diagnostic is.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
|
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
|
||||||
pub enum Level {
|
pub enum Level {
|
||||||
Warning,
|
Warning,
|
||||||
Error,
|
Error,
|
||||||
@ -103,8 +103,8 @@ impl Display for Level {
|
|||||||
|
|
||||||
/// Decorations for semantic syntax highlighting.
|
/// Decorations for semantic syntax highlighting.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
|
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
|
||||||
pub enum Deco {
|
pub enum Deco {
|
||||||
/// Emphasized text.
|
/// Emphasized text.
|
||||||
Emph,
|
Emph,
|
||||||
|
@ -9,10 +9,10 @@ pub struct Document {
|
|||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
/// Layout the document.
|
/// Layout the document.
|
||||||
pub async fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
|
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
|
||||||
let mut layouts = vec![];
|
let mut layouts = vec![];
|
||||||
for run in &self.runs {
|
for run in &self.runs {
|
||||||
layouts.extend(run.layout(ctx).await);
|
layouts.extend(run.layout(ctx));
|
||||||
}
|
}
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@ -31,9 +31,9 @@ pub struct Pages {
|
|||||||
|
|
||||||
impl Pages {
|
impl Pages {
|
||||||
/// Layout the page run.
|
/// Layout the page run.
|
||||||
pub async fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
|
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
|
||||||
let areas = Areas::repeat(self.size);
|
let areas = Areas::repeat(self.size);
|
||||||
let layouted = self.child.layout(ctx, &areas).await;
|
let layouted = self.child.layout(ctx, &areas);
|
||||||
layouted.into_iter().filter_map(Layouted::into_boxed).collect()
|
layouted.into_iter().filter_map(Layouted::into_boxed).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@ pub struct Fixed {
|
|||||||
pub child: LayoutNode,
|
pub child: LayoutNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for Fixed {
|
impl Layout for Fixed {
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
||||||
let Area { rem, full } = areas.current;
|
let Area { rem, full } = areas.current;
|
||||||
let size = Size::new(
|
let size = Size::new(
|
||||||
self.width.map(|w| w.eval(full.width)).unwrap_or(rem.width),
|
self.width.map(|w| w.eval(full.width)).unwrap_or(rem.width),
|
||||||
@ -22,7 +21,7 @@ impl Layout for Fixed {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let areas = Areas::once(size);
|
let areas = Areas::once(size);
|
||||||
self.child.layout(ctx, &areas).await
|
self.child.layout(ctx, &areas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,6 @@ mod spacing;
|
|||||||
mod stack;
|
mod stack;
|
||||||
mod text;
|
mod text;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use crate::font::SharedFontLoader;
|
use crate::font::SharedFontLoader;
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
use crate::shaping::Shaped;
|
use crate::shaping::Shaped;
|
||||||
@ -25,9 +23,9 @@ pub use stack::*;
|
|||||||
pub use text::*;
|
pub use text::*;
|
||||||
|
|
||||||
/// Layout a document and return the produced layouts.
|
/// Layout a document and return the produced layouts.
|
||||||
pub async fn layout(document: &Document, loader: SharedFontLoader) -> Vec<BoxLayout> {
|
pub fn layout(document: &Document, loader: SharedFontLoader) -> Vec<BoxLayout> {
|
||||||
let mut ctx = LayoutContext { loader };
|
let mut ctx = LayoutContext { loader };
|
||||||
document.layout(&mut ctx).await
|
document.layout(&mut ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
@ -38,20 +36,9 @@ pub struct LayoutContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a node.
|
/// Layout a node.
|
||||||
#[async_trait(?Send)]
|
|
||||||
pub trait Layout {
|
pub trait Layout {
|
||||||
/// Layout the node in the given layout context.
|
/// Layout the node in the given layout context.
|
||||||
///
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted>;
|
||||||
/// This signature looks pretty horrible due to async in trait methods, but
|
|
||||||
/// it's actually just the following:
|
|
||||||
/// ```rust,ignore
|
|
||||||
/// async fn layout(
|
|
||||||
/// &self,
|
|
||||||
/// ctx: &mut LayoutContext,
|
|
||||||
/// constraints: LayoutConstraints,
|
|
||||||
/// ) -> Vec<LayoutItem>;
|
|
||||||
/// ```
|
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sequence of areas to layout into.
|
/// A sequence of areas to layout into.
|
||||||
|
@ -24,13 +24,12 @@ impl LayoutNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for LayoutNode {
|
impl Layout for LayoutNode {
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
||||||
match self {
|
match self {
|
||||||
Self::Spacing(spacing) => spacing.layout(ctx, areas).await,
|
Self::Spacing(spacing) => spacing.layout(ctx, areas),
|
||||||
Self::Text(text) => text.layout(ctx, areas).await,
|
Self::Text(text) => text.layout(ctx, areas),
|
||||||
Self::Dyn(boxed) => boxed.layout(ctx, areas).await,
|
Self::Dyn(boxed) => boxed.layout(ctx, areas),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,8 @@ pub struct Pad {
|
|||||||
pub child: LayoutNode,
|
pub child: LayoutNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for Pad {
|
impl Layout for Pad {
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
||||||
let shrink = |size| size - self.padding.eval(size).size();
|
let shrink = |size| size - self.padding.eval(size).size();
|
||||||
let areas = Areas {
|
let areas = Areas {
|
||||||
current: Area {
|
current: Area {
|
||||||
@ -23,7 +22,7 @@ impl Layout for Pad {
|
|||||||
last: areas.last.map(shrink),
|
last: areas.last.map(shrink),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut layouted = self.child.layout(ctx, &areas).await;
|
let mut layouted = self.child.layout(ctx, &areas);
|
||||||
|
|
||||||
for item in &mut layouted {
|
for item in &mut layouted {
|
||||||
if let Layouted::Boxed(boxed, _) = item {
|
if let Layouted::Boxed(boxed, _) = item {
|
||||||
|
@ -18,12 +18,11 @@ pub struct Par {
|
|||||||
pub children: Vec<LayoutNode>,
|
pub children: Vec<LayoutNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for Par {
|
impl Layout for Par {
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
||||||
let mut layouter = ParLayouter::new(self, areas.clone());
|
let mut layouter = ParLayouter::new(self, areas.clone());
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
for layouted in child.layout(ctx, &layouter.areas).await {
|
for layouted in child.layout(ctx, &layouter.areas) {
|
||||||
match layouted {
|
match layouted {
|
||||||
Layouted::Spacing(spacing) => layouter.spacing(spacing),
|
Layouted::Spacing(spacing) => layouter.spacing(spacing),
|
||||||
Layouted::Boxed(boxed, aligns) => layouter.boxed(boxed, aligns.cross),
|
Layouted::Boxed(boxed, aligns) => layouter.boxed(boxed, aligns.cross),
|
||||||
|
@ -14,9 +14,8 @@ pub struct Spacing {
|
|||||||
pub softness: Softness,
|
pub softness: Softness,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for Spacing {
|
impl Layout for Spacing {
|
||||||
async fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
|
fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
|
||||||
vec![Layouted::Spacing(self.amount)]
|
vec![Layouted::Spacing(self.amount)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,11 @@ pub struct Stack {
|
|||||||
pub children: Vec<LayoutNode>,
|
pub children: Vec<LayoutNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for Stack {
|
impl Layout for Stack {
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
|
||||||
let mut layouter = StackLayouter::new(self, areas.clone());
|
let mut layouter = StackLayouter::new(self, areas.clone());
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
for layouted in child.layout(ctx, &layouter.areas).await {
|
for layouted in child.layout(ctx, &layouter.areas) {
|
||||||
match layouted {
|
match layouted {
|
||||||
Layouted::Spacing(spacing) => layouter.spacing(spacing),
|
Layouted::Spacing(spacing) => layouter.spacing(spacing),
|
||||||
Layouted::Boxed(boxed, aligns) => layouter.boxed(boxed, aligns),
|
Layouted::Boxed(boxed, aligns) => layouter.boxed(boxed, aligns),
|
||||||
|
@ -23,9 +23,8 @@ pub struct Text {
|
|||||||
pub aligns: Gen<Align>,
|
pub aligns: Gen<Align>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl Layout for Text {
|
impl Layout for Text {
|
||||||
async fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
|
fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
|
||||||
let mut loader = ctx.loader.borrow_mut();
|
let mut loader = ctx.loader.borrow_mut();
|
||||||
vec![Layouted::Boxed(
|
vec![Layouted::Boxed(
|
||||||
shaping::shape(
|
shaping::shape(
|
||||||
|
@ -50,13 +50,13 @@ use crate::font::SharedFontLoader;
|
|||||||
use crate::layout::BoxLayout;
|
use crate::layout::BoxLayout;
|
||||||
|
|
||||||
/// Process _Typst_ source code directly into a collection of layouts.
|
/// Process _Typst_ source code directly into a collection of layouts.
|
||||||
pub async fn typeset(
|
pub fn typeset(
|
||||||
src: &str,
|
src: &str,
|
||||||
state: State,
|
state: State,
|
||||||
loader: SharedFontLoader,
|
loader: SharedFontLoader,
|
||||||
) -> Pass<Vec<BoxLayout>> {
|
) -> Pass<Vec<BoxLayout>> {
|
||||||
let Pass { output: tree, feedback: f1 } = parse::parse(src);
|
let Pass { output: tree, feedback: f1 } = parse::parse(src);
|
||||||
let Pass { output: document, feedback: f2 } = eval::eval(&tree, state);
|
let Pass { output: document, feedback: f2 } = eval::eval(&tree, state);
|
||||||
let layouts = layout::layout(&document, loader).await;
|
let layouts = layout::layout(&document, loader);
|
||||||
Pass::new(layouts, Feedback::join(f1, f2))
|
Pass::new(layouts, Feedback::join(f1, f2))
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ impl<T> Offset for SpanVec<T> {
|
|||||||
|
|
||||||
/// A value with the span it corresponds to in the source code.
|
/// A value with the span it corresponds to in the source code.
|
||||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
pub struct Spanned<T> {
|
pub struct Spanned<T> {
|
||||||
/// The spanned value.
|
/// The spanned value.
|
||||||
pub v: T,
|
pub v: T,
|
||||||
@ -109,7 +109,7 @@ impl<T: Debug> Debug for Spanned<T> {
|
|||||||
|
|
||||||
/// Locates a slice of source code.
|
/// Locates a slice of source code.
|
||||||
#[derive(Copy, Clone, Ord, PartialOrd)]
|
#[derive(Copy, Clone, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
/// The inclusive start position.
|
/// The inclusive start position.
|
||||||
pub start: Pos,
|
pub start: Pos,
|
||||||
@ -210,7 +210,7 @@ impl Debug for Span {
|
|||||||
|
|
||||||
/// A byte position in source code.
|
/// A byte position in source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
pub struct Pos(pub u32);
|
pub struct Pos(pub u32);
|
||||||
|
|
||||||
impl Pos {
|
impl Pos {
|
||||||
@ -255,7 +255,7 @@ impl Debug for Pos {
|
|||||||
|
|
||||||
/// A one-indexed line-column position in source code.
|
/// A one-indexed line-column position in source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
/// The one-indexed line.
|
/// The one-indexed line.
|
||||||
pub line: u32,
|
pub line: u32,
|
||||||
|
@ -7,7 +7,6 @@ use std::path::Path;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use fontdock::fs::{FsIndex, FsSource};
|
use fontdock::fs::{FsIndex, FsSource};
|
||||||
use futures_executor::block_on;
|
|
||||||
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
|
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
|
||||||
use ttf_parser::OutlineBuilder;
|
use ttf_parser::OutlineBuilder;
|
||||||
|
|
||||||
@ -77,7 +76,7 @@ fn test(name: &str, src: &str, src_path: &Path, loader: &SharedFontLoader) {
|
|||||||
let Pass {
|
let Pass {
|
||||||
output: layouts,
|
output: layouts,
|
||||||
feedback: Feedback { mut diags, .. },
|
feedback: Feedback { mut diags, .. },
|
||||||
} = block_on(typeset(&src, state, Rc::clone(loader)));
|
} = typeset(&src, state, Rc::clone(loader));
|
||||||
|
|
||||||
if !diags.is_empty() {
|
if !diags.is_empty() {
|
||||||
diags.sort();
|
diags.sort();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user