Refactor layouting base 🪁

This commit is contained in:
Laurenz 2020-10-11 22:38:30 +02:00
parent f04ad0ffa5
commit d3bc4ec073
14 changed files with 224 additions and 189 deletions

View File

@ -1,7 +1,5 @@
//! Simplifies argument parsing. //! Simplifies argument parsing.
use std::mem;
use super::{Convert, EvalContext, RefKey, ValueDict}; use super::{Convert, EvalContext, RefKey, ValueDict};
use crate::syntax::{SpanWith, Spanned}; use crate::syntax::{SpanWith, Spanned};
@ -67,7 +65,7 @@ impl Args {
{ {
for (&key, entry) in self.0.v.nums_mut() { for (&key, entry) in self.0.v.nums_mut() {
let span = entry.value.span; let span = entry.value.span;
match T::convert(mem::take(&mut entry.value)).0 { match T::convert(std::mem::take(&mut entry.value)).0 {
Ok(t) => { Ok(t) => {
self.0.v.remove(key); self.0.v.remove(key);
return Some(t); return Some(t);
@ -87,7 +85,7 @@ impl Args {
std::iter::from_fn(move || { std::iter::from_fn(move || {
for (&key, entry) in self.0.v.nums_mut().skip(skip) { for (&key, entry) in self.0.v.nums_mut().skip(skip) {
let span = entry.value.span; let span = entry.value.span;
match T::convert(mem::take(&mut entry.value)).0 { match T::convert(std::mem::take(&mut entry.value)).0 {
Ok(t) => { Ok(t) => {
self.0.v.remove(key); self.0.v.remove(key);
return Some(t); return Some(t);
@ -109,7 +107,7 @@ impl Args {
std::iter::from_fn(move || { std::iter::from_fn(move || {
for (key, entry) in self.0.v.strs_mut().skip(skip) { for (key, entry) in self.0.v.strs_mut().skip(skip) {
let span = entry.value.span; let span = entry.value.span;
match T::convert(mem::take(&mut entry.value)).0 { match T::convert(std::mem::take(&mut entry.value)).0 {
Ok(t) => { Ok(t) => {
let key = key.clone(); let key = key.clone();
self.0.v.remove(&key); self.0.v.remove(&key);

View File

@ -16,16 +16,15 @@ pub use state::*;
pub use value::*; pub use value::*;
use std::any::Any; use std::any::Any;
use std::mem;
use std::rc::Rc; use std::rc::Rc;
use fontdock::FontStyle; use fontdock::FontStyle;
use crate::diag::Diag; use crate::diag::Diag;
use crate::diag::{Deco, Feedback, Pass}; use crate::diag::{Deco, Feedback, Pass};
use crate::geom::{Gen, Length, Relative, Spec, Switch}; use crate::geom::{Gen, Length, Relative};
use crate::layout::{ use crate::layout::{
Document, LayoutNode, Pad, Pages, Par, Softness, Spacing, Stack, Text, Document, Expansion, LayoutNode, Pad, Pages, Par, Softness, Spacing, Stack, Text,
}; };
use crate::syntax::*; use crate::syntax::*;
@ -35,11 +34,9 @@ use crate::syntax::*;
/// evaluation. /// evaluation.
pub fn eval(tree: &SynTree, state: State) -> Pass<Document> { pub fn eval(tree: &SynTree, state: State) -> Pass<Document> {
let mut ctx = EvalContext::new(state); let mut ctx = EvalContext::new(state);
ctx.start_page_group(false); ctx.start_page_group(false);
tree.eval(&mut ctx); tree.eval(&mut ctx);
ctx.end_page_group(); ctx.end_page_group();
ctx.finish() ctx.finish()
} }
@ -125,7 +122,7 @@ impl EvalContext {
/// [`push`]: #method.push /// [`push`]: #method.push
/// [`end_group`]: #method.end_group /// [`end_group`]: #method.end_group
pub fn start_group<T: 'static>(&mut self, meta: T) { pub fn start_group<T: 'static>(&mut self, meta: T) {
self.groups.push((Box::new(meta), mem::take(&mut self.inner))); self.groups.push((Box::new(meta), std::mem::take(&mut self.inner)));
} }
/// End a layouting group started with [`start_group`]. /// End a layouting group started with [`start_group`].
@ -134,9 +131,15 @@ impl EvalContext {
/// ///
/// [`start_group`]: #method.start_group /// [`start_group`]: #method.start_group
pub fn end_group<T: 'static>(&mut self) -> (T, Vec<LayoutNode>) { pub fn end_group<T: 'static>(&mut self) -> (T, Vec<LayoutNode>) {
if let Some(&LayoutNode::Spacing(spacing)) = self.inner.last() {
if spacing.softness == Softness::Soft {
self.inner.pop();
}
}
let (any, outer) = self.groups.pop().expect("no pushed group"); let (any, outer) = self.groups.pop().expect("no pushed group");
let group = *any.downcast::<T>().expect("bad group type"); let group = *any.downcast::<T>().expect("bad group type");
(group, mem::replace(&mut self.inner, outer)) (group, std::mem::replace(&mut self.inner, outer))
} }
/// Start a page run group based on the active page state. /// Start a page run group based on the active page state.
@ -167,9 +170,9 @@ impl EvalContext {
padding, padding,
child: LayoutNode::dynamic(Stack { child: LayoutNode::dynamic(Stack {
dirs, dirs,
children,
aligns, aligns,
expand: Spec::new(true, true), expansion: Gen::new(Expansion::Fill, Expansion::Fill),
children,
}), }),
}), }),
}) })
@ -191,13 +194,13 @@ impl EvalContext {
if !children.is_empty() { if !children.is_empty() {
// FIXME: This is a hack and should be superseded by constraints // FIXME: This is a hack and should be superseded by constraints
// having min and max size. // having min and max size.
let expand_cross = self.groups.len() <= 1; let cross_expansion = Expansion::fill_if(self.groups.len() <= 1);
self.push(Par { self.push(Par {
dirs, dirs,
aligns,
cross_expansion,
line_spacing, line_spacing,
children, children,
aligns,
expand: Gen::new(false, expand_cross).switch(dirs),
}); });
} }
} }
@ -222,7 +225,7 @@ impl EvalContext {
Text { Text {
text, text,
dir: self.state.dirs.cross, dir: self.state.dirs.cross,
size: self.state.font.font_size(), font_size: self.state.font.font_size(),
families: Rc::clone(&self.state.font.families), families: Rc::clone(&self.state.font.families),
variant, variant,
aligns: self.state.aligns, aligns: self.state.aligns,
@ -284,26 +287,11 @@ impl Eval for SynNode {
ctx.start_par_group(); ctx.start_par_group();
} }
SynNode::Emph => { SynNode::Emph => ctx.state.font.emph ^= true,
ctx.state.font.emph ^= true; SynNode::Strong => ctx.state.font.strong ^= true,
} SynNode::Heading(heading) => heading.eval(ctx),
SynNode::Raw(raw) => raw.eval(ctx),
SynNode::Strong => { SynNode::Expr(expr) => expr.eval(ctx).eval(ctx),
ctx.state.font.strong ^= true;
}
SynNode::Heading(heading) => {
heading.eval(ctx);
}
SynNode::Raw(raw) => {
raw.eval(ctx);
}
SynNode::Expr(expr) => {
let value = expr.eval(ctx);
value.eval(ctx);
}
} }
} }
} }
@ -339,9 +327,9 @@ impl Eval for NodeRaw {
ctx.push(Stack { ctx.push(Stack {
dirs: ctx.state.dirs, dirs: ctx.state.dirs,
children,
aligns: ctx.state.aligns, aligns: ctx.state.aligns,
expand: Spec::new(false, false), expansion: Gen::new(Expansion::Fit, Expansion::Fit),
children,
}); });
ctx.state.font.families = prev; ctx.state.font.families = prev;

View File

@ -142,9 +142,9 @@ impl<'a, W: Write> PdfExporter<'a, W> {
match element { match element {
LayoutElement::Text(shaped) => { LayoutElement::Text(shaped) => {
// Check if we need to issue a font switching action. // Check if we need to issue a font switching action.
if shaped.face != face || shaped.size != size { if shaped.face != face || shaped.font_size != size {
face = shaped.face; face = shaped.face;
size = shaped.size; size = shaped.font_size;
text.tf( text.tf(
self.to_pdf[&shaped.face] as u32 + 1, self.to_pdf[&shaped.face] as u32 + 1,
size.to_pt() as f32, size.to_pt() as f32,

View File

@ -3,6 +3,7 @@ use super::*;
/// The top-level layout node. /// The top-level layout node.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Document { pub struct Document {
/// The runs of pages with same properties.
pub runs: Vec<Pages>, pub runs: Vec<Pages>,
} }
@ -22,26 +23,17 @@ impl Document {
pub struct Pages { pub struct Pages {
/// The size of the pages. /// The size of the pages.
pub size: Size, pub size: Size,
/// The layout node that produces the actual pages. /// The layout node that produces the actual pages (typically a [stack]).
///
/// [stack]: struct.Stack.html
pub child: LayoutNode, pub child: LayoutNode,
} }
impl Pages { impl Pages {
/// Layout the page run. /// Layout the page run.
pub async fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> { pub async fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
let constraints = LayoutConstraints { let areas = Areas::repeat(self.size);
spaces: vec![LayoutSpace { base: self.size, size: self.size }], let layouted = self.child.layout(ctx, &areas).await;
repeat: true, layouted.into_iter().filter_map(Layouted::into_boxed).collect()
};
self.child
.layout(ctx, constraints)
.await
.into_iter()
.filter_map(|item| match item {
Layouted::Spacing(_) => None,
Layouted::Box(layout, _) => Some(layout),
})
.collect()
} }
} }

View File

@ -4,34 +4,25 @@ use crate::geom::Linear;
/// A node that can fix its child's width and height. /// A node that can fix its child's width and height.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Fixed { pub struct Fixed {
/// The fixed width, if any.
pub width: Option<Linear>, pub width: Option<Linear>,
/// The fixed height, if any.
pub height: Option<Linear>, pub height: Option<Linear>,
/// The child node whose size to fix.
pub child: LayoutNode, pub child: LayoutNode,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Layout for Fixed { impl Layout for Fixed {
async fn layout( async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
&self, let Area { rem, full } = areas.current;
ctx: &mut LayoutContext,
constraints: LayoutConstraints,
) -> Vec<Layouted> {
let space = constraints.spaces[0];
let size = Size::new( let size = Size::new(
self.width self.width.map(|w| w.eval(full.width)).unwrap_or(rem.width),
.map(|w| w.eval(space.base.width)) self.height.map(|h| h.eval(full.height)).unwrap_or(rem.height),
.unwrap_or(space.size.width),
self.height
.map(|h| h.eval(space.base.height))
.unwrap_or(space.size.height),
); );
self.child let areas = Areas::once(size);
.layout(ctx, LayoutConstraints { self.child.layout(ctx, &areas).await
spaces: vec![LayoutSpace { base: size, size }],
repeat: false,
})
.await
} }
} }

View File

@ -51,11 +51,86 @@ pub trait Layout {
/// constraints: LayoutConstraints, /// constraints: LayoutConstraints,
/// ) -> Vec<LayoutItem>; /// ) -> Vec<LayoutItem>;
/// ``` /// ```
async fn layout( async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted>;
&self, }
ctx: &mut LayoutContext,
constraints: LayoutConstraints, /// A sequence of areas to layout into.
) -> Vec<Layouted>; #[derive(Debug, Clone, PartialEq)]
pub struct Areas {
/// The current area.
pub current: Area,
/// The backlog of followup areas.
///
/// _Note_: This works stack-like and not queue-like!
pub backlog: Vec<Size>,
/// The last area that is repeated when the backlog is empty.
pub last: Option<Size>,
}
impl Areas {
/// Create a new length-1 sequence of areas with just one `area`.
pub fn once(size: Size) -> Self {
Self {
current: Area::new(size),
backlog: vec![],
last: None,
}
}
/// Create a new sequence of areas that repeats `area` indefinitely.
pub fn repeat(size: Size) -> Self {
Self {
current: Area::new(size),
backlog: vec![],
last: Some(size),
}
}
/// Advance to the next area if there is any.
pub fn next(&mut self) {
if let Some(size) = self.backlog.pop().or(self.last) {
self.current = Area::new(size);
}
}
/// Whether `current` is a fully sized (untouched) copy of the last area.
///
/// If this is false calling `next()` will have no effect.
pub fn in_full_last(&self) -> bool {
self.backlog.is_empty() && self.last.map_or(true, |size| self.current.rem == size)
}
}
/// The area into which content can be laid out.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Area {
/// The remaining size of this area.
pub rem: Size,
/// The full size this area once had (used for relative sizing).
pub full: Size,
}
impl Area {
/// Create a new area.
pub fn new(size: Size) -> Self {
Self { rem: size, full: size }
}
}
/// How to determine a container's size along an axis.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Expansion {
/// Fit the content.
Fit,
/// Fill the available space.
Fill,
}
impl Expansion {
/// Returns `Fill` if the condition is true and `Fit` otherwise.
pub fn fill_if(condition: bool) -> Self {
if condition { Self::Fill } else { Self::Fit }
}
} }
/// An item that is produced by [layouting] a node. /// An item that is produced by [layouting] a node.
@ -65,27 +140,18 @@ pub trait Layout {
pub enum Layouted { pub enum Layouted {
/// Spacing that should be added to the parent. /// Spacing that should be added to the parent.
Spacing(Length), Spacing(Length),
/// A box that should be aligned in the parent. /// A box that should be added to and aligned in the parent.
Box(BoxLayout, Gen<Align>), Boxed(BoxLayout, Gen<Align>),
} }
/// The constraints for layouting a single node. impl Layouted {
#[derive(Debug, Clone)] /// Return the box if this if its a box variant.
pub struct LayoutConstraints { pub fn into_boxed(self) -> Option<BoxLayout> {
/// The spaces to layout into. match self {
pub spaces: Vec<LayoutSpace>, Self::Spacing(_) => None,
/// Whether to spill over into copies of the last space or finish layouting Self::Boxed(boxed, _) => Some(boxed),
/// when the last space is used up. }
pub repeat: bool, }
}
/// The space into which content is laid out.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LayoutSpace {
/// The full size of this container (the base for relative sizes).
pub base: Size,
/// The maximum size of the rectangle to layout into.
pub size: Size,
} }
/// A finished box with content at fixed positions. /// A finished box with content at fixed positions.

View File

@ -18,12 +18,23 @@ pub enum LayoutNode {
} }
impl LayoutNode { impl LayoutNode {
/// Create a new model node form a type implementing `DynNode`. /// Create a new dynamic node.
pub fn dynamic<T: DynNode>(inner: T) -> Self { pub fn dynamic<T: DynNode>(inner: T) -> Self {
Self::Dyn(Dynamic::new(inner)) Self::Dyn(Dynamic::new(inner))
} }
} }
#[async_trait(?Send)]
impl Layout for LayoutNode {
async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
match self {
Self::Spacing(spacing) => spacing.layout(ctx, areas).await,
Self::Text(text) => text.layout(ctx, areas).await,
Self::Dyn(boxed) => boxed.layout(ctx, areas).await,
}
}
}
impl Debug for LayoutNode { impl Debug for LayoutNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
@ -34,21 +45,6 @@ impl Debug for LayoutNode {
} }
} }
#[async_trait(?Send)]
impl Layout for LayoutNode {
async fn layout(
&self,
ctx: &mut LayoutContext,
constraints: LayoutConstraints,
) -> Vec<Layouted> {
match self {
Self::Spacing(spacing) => spacing.layout(ctx, constraints).await,
Self::Text(text) => text.layout(ctx, constraints).await,
Self::Dyn(boxed) => boxed.layout(ctx, constraints).await,
}
}
}
/// A wrapper around a boxed dynamic node. /// A wrapper around a boxed dynamic node.
/// ///
/// _Note_: This is needed because the compiler can't `derive(PartialEq)` for /// _Note_: This is needed because the compiler can't `derive(PartialEq)` for
@ -80,6 +76,12 @@ impl Debug for Dynamic {
} }
} }
impl From<Dynamic> for LayoutNode {
fn from(dynamic: Dynamic) -> Self {
Self::Dyn(dynamic)
}
}
impl Clone for Dynamic { impl Clone for Dynamic {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0.dyn_clone()) Self(self.0.dyn_clone())
@ -92,12 +94,6 @@ impl PartialEq for Dynamic {
} }
} }
impl From<Dynamic> for LayoutNode {
fn from(dynamic: Dynamic) -> Self {
Self::Dyn(dynamic)
}
}
/// A dynamic node, which can implement custom layouting behaviour. /// A dynamic node, which can implement custom layouting behaviour.
/// ///
/// This trait just combines the requirements for types to qualify as dynamic /// This trait just combines the requirements for types to qualify as dynamic

View File

@ -4,45 +4,40 @@ use crate::geom::Linear;
/// A node that pads its child at the sides. /// A node that pads its child at the sides.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Pad { pub struct Pad {
/// The amount of padding.
pub padding: Sides<Linear>, pub padding: Sides<Linear>,
/// The child node whose sides to pad.
pub child: LayoutNode, pub child: LayoutNode,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Layout for Pad { impl Layout for Pad {
async fn layout( async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
&self, let shrink = |size| size - self.padding.eval(size).size();
ctx: &mut LayoutContext, let areas = Areas {
constraints: LayoutConstraints, current: Area {
) -> Vec<Layouted> { rem: shrink(areas.current.rem),
self.child full: shrink(areas.current.full),
.layout(ctx, LayoutConstraints { },
spaces: constraints backlog: areas.backlog.iter().copied().map(shrink).collect(),
.spaces last: areas.last.map(shrink),
.into_iter() };
.map(|space| LayoutSpace {
base: space.base - self.padding.eval(space.base).size(),
size: space.size - self.padding.eval(space.size).size(),
})
.collect(),
repeat: constraints.repeat,
})
.await
.into_iter()
.map(|item| match item {
Layouted::Box(boxed, align) => {
let padding = self.padding.eval(boxed.size);
let padded = boxed.size + padding.size();
let mut outer = BoxLayout::new(padded); let mut layouted = self.child.layout(ctx, &areas).await;
let start = Point::new(padding.left, padding.top);
outer.push_layout(start, boxed);
Layouted::Box(outer, align) for item in &mut layouted {
if let Layouted::Boxed(boxed, _) = item {
let padding = self.padding.eval(boxed.size);
let origin = Point::new(padding.left, padding.top);
boxed.size += padding.size();
for (point, _) in &mut boxed.elements {
*point += origin;
} }
item => item, }
}) }
.collect()
layouted
} }
} }

View File

@ -2,16 +2,21 @@ use std::fmt::{self, Debug, Formatter};
use super::*; use super::*;
/// A node that inserts spacing. /// A spacing node.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct Spacing { pub struct Spacing {
/// The amount of spacing to insert.
pub amount: Length, pub amount: Length,
/// Spacing interaction, see [Softness's] documentation for more
/// information.
///
/// [Softness's]: enum.Softness.html
pub softness: Softness, pub softness: Softness,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Layout for Spacing { impl Layout for Spacing {
async fn layout(&self, _: &mut LayoutContext, _: LayoutConstraints) -> Vec<Layouted> { async fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
vec![Layouted::Spacing(self.amount)] vec![Layouted::Spacing(self.amount)]
} }
} }

View File

@ -9,32 +9,36 @@ use crate::shaping;
/// A text node. /// A text node.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct Text { pub struct Text {
/// The text.
pub text: String, pub text: String,
pub size: Length, /// The font size.
pub font_size: Length,
/// The text direction.
pub dir: Dir, pub dir: Dir,
/// The families used for font fallback.
pub families: Rc<FallbackTree>, pub families: Rc<FallbackTree>,
/// The font variant,
pub variant: FontVariant, pub variant: FontVariant,
/// How to align this text node in its parent.
pub aligns: Gen<Align>, pub aligns: Gen<Align>,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl Layout for Text { impl Layout for Text {
async fn layout( async fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
&self,
ctx: &mut LayoutContext,
_constraints: LayoutConstraints,
) -> Vec<Layouted> {
let mut loader = ctx.loader.borrow_mut(); let mut loader = ctx.loader.borrow_mut();
let boxed = shaping::shape( vec![Layouted::Boxed(
&self.text, shaping::shape(
self.size, &mut loader,
self.dir, &self.text,
&mut loader, self.font_size,
&self.families, self.dir,
self.variant, &self.families,
) self.variant,
.await; )
vec![Layouted::Box(boxed, self.aligns)] .await,
self.aligns,
)]
} }
} }

View File

@ -1,5 +1,5 @@
use crate::geom::Linear; use crate::geom::Linear;
use crate::layout::{Fixed, Stack}; use crate::layout::{Expansion, Fixed, Stack};
use crate::prelude::*; use crate::prelude::*;
/// `box`: Layouts its contents into a box. /// `box`: Layouts its contents into a box.
@ -20,9 +20,7 @@ pub fn boxed(mut args: Args, ctx: &mut EvalContext) -> Value {
ctx.start_group(()); ctx.start_group(());
ctx.start_par_group(); ctx.start_par_group();
body.eval(ctx); body.eval(ctx);
ctx.end_par_group(); ctx.end_par_group();
let ((), children) = ctx.end_group(); let ((), children) = ctx.end_group();
@ -33,7 +31,11 @@ pub fn boxed(mut args: Args, ctx: &mut EvalContext) -> Value {
dirs, dirs,
children, children,
aligns, aligns,
expand: Spec::new(width.is_some(), height.is_some()), expansion: Spec::new(
Expansion::fill_if(width.is_some()),
Expansion::fill_if(height.is_some()),
)
.switch(dirs),
}), }),
}); });

View File

@ -1,5 +1,3 @@
use std::mem;
use crate::geom::{Length, Linear}; use crate::geom::{Length, Linear};
use crate::paper::{Paper, PaperClass}; use crate::paper::{Paper, PaperClass};
use crate::prelude::*; use crate::prelude::*;
@ -56,7 +54,7 @@ pub fn page(mut args: Args, ctx: &mut EvalContext) -> Value {
if args.get::<_, bool>(ctx, "flip").unwrap_or(false) { if args.get::<_, bool>(ctx, "flip").unwrap_or(false) {
let size = &mut ctx.state.page.size; let size = &mut ctx.state.page.size;
mem::swap(&mut size.width, &mut size.height); std::mem::swap(&mut size.width, &mut size.height);
} }
args.done(ctx); args.done(ctx);

View File

@ -22,11 +22,11 @@ pub struct Shaped {
pub face: FaceId, pub face: FaceId,
/// The shaped glyphs. /// The shaped glyphs.
pub glyphs: Vec<GlyphId>, pub glyphs: Vec<GlyphId>,
/// The horizontal offsets of the glyphs. This is indexed parallel to `glyphs`. /// The horizontal offsets of the glyphs. This is indexed parallel to
/// Vertical offets are not yet supported. /// `glyphs`. Vertical offets are not yet supported.
pub offsets: Vec<Length>, pub offsets: Vec<Length>,
/// The font size. /// The font size.
pub size: Length, pub font_size: Length,
} }
impl Shaped { impl Shaped {
@ -37,7 +37,7 @@ impl Shaped {
face, face,
glyphs: vec![], glyphs: vec![],
offsets: vec![], offsets: vec![],
size, font_size: size,
} }
} }
@ -62,15 +62,15 @@ impl Debug for Shaped {
/// ///
/// [`Shaped`]: struct.Shaped.html /// [`Shaped`]: struct.Shaped.html
pub async fn shape( pub async fn shape(
text: &str,
size: Length,
dir: Dir,
loader: &mut FontLoader, loader: &mut FontLoader,
text: &str,
font_size: Length,
dir: Dir,
fallback: &FallbackTree, fallback: &FallbackTree,
variant: FontVariant, variant: FontVariant,
) -> BoxLayout { ) -> BoxLayout {
let mut layout = BoxLayout::new(Size::new(Length::ZERO, size)); let mut layout = BoxLayout::new(Size::new(Length::ZERO, font_size));
let mut shaped = Shaped::new(FaceId::MAX, size); let mut shaped = Shaped::new(FaceId::MAX, font_size);
let mut offset = Length::ZERO; let mut offset = Length::ZERO;
// Create an iterator with conditional direction. // Create an iterator with conditional direction.
@ -86,7 +86,7 @@ pub async fn shape(
let query = FaceQuery { fallback: fallback.iter(), variant, c }; let query = FaceQuery { fallback: fallback.iter(), variant, c };
if let Some((id, owned_face)) = loader.query(query).await { if let Some((id, owned_face)) = loader.query(query).await {
let face = owned_face.get(); let face = owned_face.get();
let (glyph, width) = match lookup_glyph(face, c, size) { let (glyph, width) = match lookup_glyph(face, c, font_size) {
Some(v) => v, Some(v) => v,
None => continue, None => continue,
}; };
@ -96,7 +96,7 @@ pub async fn shape(
let pos = Point::new(layout.size.width, Length::ZERO); let pos = Point::new(layout.size.width, Length::ZERO);
layout.push(pos, LayoutElement::Text(shaped)); layout.push(pos, LayoutElement::Text(shaped));
layout.size.width += offset; layout.size.width += offset;
shaped = Shaped::new(FaceId::MAX, size); shaped = Shaped::new(FaceId::MAX, font_size);
offset = Length::ZERO; offset = Length::ZERO;
} }

View File

@ -198,9 +198,9 @@ fn render_shaped(
let path = builder.0.finish(); let path = builder.0.finish();
let units_per_em = face.units_per_em().unwrap_or(1000); let units_per_em = face.units_per_em().unwrap_or(1000);
let s = scale * (shaped.size / units_per_em as f64); let s = scale * (shaped.font_size / units_per_em as f64);
let x = pos.x + scale * offset; let x = pos.x + scale * offset;
let y = pos.y + scale * shaped.size; let y = pos.y + scale * shaped.font_size;
let t = Transform::create_scale(s.to_pt() as f32, -s.to_pt() as f32) let t = Transform::create_scale(s.to_pt() as f32, -s.to_pt() as f32)
.post_translate(Vector::new(x.to_pt() as f32, y.to_pt() as f32)); .post_translate(Vector::new(x.to_pt() as f32, y.to_pt() as f32));