diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-12-09 13:42:52 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-12-09 13:42:52 +0100 |
| commit | fe21c4d399d291e75165b664762f0aa8bdc4724a (patch) | |
| tree | a3ec954df6e66f6504f4416b37600cedf95dd7e1 /src/library | |
| parent | 40b87d4066fe85cb3fde6cf84cd60d748273ae25 (diff) | |
Set Rules Episode III: Revenge of the packer
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/align.rs | 11 | ||||
| -rw-r--r-- | src/library/deco.rs | 4 | ||||
| -rw-r--r-- | src/library/flow.rs | 9 | ||||
| -rw-r--r-- | src/library/page.rs | 33 | ||||
| -rw-r--r-- | src/library/par.rs | 28 | ||||
| -rw-r--r-- | src/library/text.rs | 52 |
6 files changed, 80 insertions, 57 deletions
diff --git a/src/library/align.rs b/src/library/align.rs index 76db7fc4..96a1c6c5 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -1,12 +1,19 @@ use super::prelude::*; +use super::ParNode; /// `align`: Configure the alignment along the layouting axes. pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let aligns = args.expect::<Spec<_>>("alignment")?; let body = args.expect::<Node>("body")?; - // TODO(set): Style paragraphs with x alignment. - Ok(Value::block(body.into_block().aligned(aligns))) + let mut styles = Styles::new(); + if let Some(align) = aligns.x { + styles.set(ParNode::ALIGN, align); + } + + Ok(Value::block( + body.into_block().styled(styles).aligned(aligns), + )) } /// A node that aligns its child. diff --git a/src/library/deco.rs b/src/library/deco.rs index b1ca030a..d12f60b0 100644 --- a/src/library/deco.rs +++ b/src/library/deco.rs @@ -22,7 +22,7 @@ fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> { let offset = args.named("offset")?; let extent = args.named("extent")?.unwrap_or_default(); let body: Node = args.expect("body")?; - Ok(Value::Node(body.decorate(Decoration::Line( + Ok(Value::Node(body.decorated(Decoration::Line( LineDecoration { kind, stroke, thickness, offset, extent }, )))) } @@ -37,7 +37,7 @@ pub fn link(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } Node::Text(text.into()) }); - Ok(Value::Node(body.decorate(Decoration::Link(url)))) + Ok(Value::Node(body.decorated(Decoration::Link(url)))) } /// A decoration for a frame. diff --git a/src/library/flow.rs b/src/library/flow.rs index dddd38a4..41760e51 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Debug, Formatter}; use super::prelude::*; -use super::{AlignNode, PlacedNode, Spacing}; +use super::{AlignNode, ParNode, PlacedNode, Spacing}; /// `flow`: A vertical flow of paragraphs and other layout nodes. pub fn flow(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { @@ -158,6 +158,13 @@ impl<'a> FlowLayouter<'a> { /// Layout a node. fn layout_node(&mut self, ctx: &mut LayoutContext, node: &PackedNode) { + // Add paragraph spacing. + // TODO(set): Handle edge cases. + if !self.items.is_empty() { + let spacing = node.styles.chain(&ctx.styles).get(ParNode::SPACING); + self.layout_absolute(spacing.into()); + } + if let Some(placed) = node.downcast::<PlacedNode>() { let frame = node.layout(ctx, &self.regions).remove(0); if placed.out_of_flow() { diff --git a/src/library/page.rs b/src/library/page.rs index a4ad84f6..490eef66 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -49,7 +49,12 @@ pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> { /// Layouts its child onto one or multiple pages. #[derive(Debug, Hash)] -pub struct PageNode(pub PackedNode); +pub struct PageNode { + /// The node producing the content. + pub node: PackedNode, + /// The page's styles. + pub styles: Styles, +} properties! { PageNode, @@ -77,30 +82,31 @@ properties! { impl PageNode { /// Layout the page run into a sequence of frames, one per page. pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { - // TODO(set): Take styles as parameter. - let styles = Styles::new(); + // TODO(set): Use chaining. + let prev = std::mem::replace(&mut ctx.styles, self.styles.clone()); + ctx.styles.apply(&prev); // When one of the lengths is infinite the page fits its content along // that axis. - let width = styles.get(Self::WIDTH).unwrap_or(Length::inf()); - let height = styles.get(Self::HEIGHT).unwrap_or(Length::inf()); + let width = ctx.styles.get(Self::WIDTH).unwrap_or(Length::inf()); + let height = ctx.styles.get(Self::HEIGHT).unwrap_or(Length::inf()); let mut size = Size::new(width, height); - if styles.get(Self::FLIPPED) { + if ctx.styles.get(Self::FLIPPED) { std::mem::swap(&mut size.x, &mut size.y); } // Determine the margins. - let class = styles.get(Self::CLASS); + let class = ctx.styles.get(Self::CLASS); let default = class.default_margins(); let padding = Sides { - left: styles.get(Self::LEFT).unwrap_or(default.left), - right: styles.get(Self::RIGHT).unwrap_or(default.right), - top: styles.get(Self::TOP).unwrap_or(default.top), - bottom: styles.get(Self::BOTTOM).unwrap_or(default.bottom), + left: ctx.styles.get(Self::LEFT).unwrap_or(default.left), + right: ctx.styles.get(Self::RIGHT).unwrap_or(default.right), + top: ctx.styles.get(Self::TOP).unwrap_or(default.top), + bottom: ctx.styles.get(Self::BOTTOM).unwrap_or(default.bottom), }; // Pad the child. - let padded = PadNode { child: self.0.clone(), padding }.pack(); + let padded = PadNode { child: self.node.clone(), padding }.pack(); // Layout the child. let expand = size.map(Length::is_finite); @@ -109,13 +115,14 @@ impl PageNode { padded.layout(ctx, ®ions).into_iter().map(|c| c.item).collect(); // Add background fill if requested. - if let Some(fill) = styles.get(Self::FILL) { + if let Some(fill) = ctx.styles.get(Self::FILL) { for frame in &mut frames { let shape = Shape::filled(Geometry::Rect(frame.size), fill); Rc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); } } + ctx.styles = prev; frames } } diff --git a/src/library/par.rs b/src/library/par.rs index 21760225..e7433e3e 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -73,19 +73,16 @@ impl Layout for ParNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - // TODO(set): Take styles as parameter. - let styles = Styles::new(); - // Collect all text into one string used for BiDi analysis. let text = self.collect_text(); // Find out the BiDi embedding levels. - let default_level = Level::from_dir(styles.get(Self::DIR)); + let default_level = Level::from_dir(ctx.styles.get(Self::DIR)); let bidi = BidiInfo::new(&text, default_level); // Prepare paragraph layout by building a representation on which we can // do line breaking without layouting each and every line from scratch. - let layouter = ParLayouter::new(self, ctx, regions, bidi, &styles); + let layouter = ParLayouter::new(self, ctx, regions, bidi); // Find suitable linebreaks. layouter.layout(ctx, regions.clone()) @@ -119,8 +116,8 @@ impl ParNode { fn strings(&self) -> impl Iterator<Item = &str> { self.0.iter().map(|child| match child { ParChild::Spacing(_) => " ", - ParChild::Text(ref piece, ..) => &piece.0, - ParChild::Node(..) => "\u{FFFC}", + ParChild::Text(ref node) => &node.text, + ParChild::Node(_) => "\u{FFFC}", ParChild::Decorate(_) | ParChild::Undecorate => "", }) } @@ -132,7 +129,6 @@ pub enum ParChild { /// Spacing between other nodes. Spacing(Spacing), /// A run of text and how to align it in its line. - // TODO(set): A single text run may also have its own style. Text(TextNode), /// Any child node and how to align it in its line. Node(PackedNode), @@ -146,7 +142,7 @@ impl Debug for ParChild { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::Spacing(v) => write!(f, "Spacing({:?})", v), - Self::Text(text) => write!(f, "Text({:?})", text), + Self::Text(node) => write!(f, "Text({:?})", node.text), Self::Node(node) => node.fmt(f), Self::Decorate(deco) => write!(f, "Decorate({:?})", deco), Self::Undecorate => write!(f, "Undecorate"), @@ -193,7 +189,6 @@ impl<'a> ParLayouter<'a> { ctx: &mut LayoutContext, regions: &Regions, bidi: BidiInfo<'a>, - styles: &'a Styles, ) -> Self { let mut items = vec![]; let mut ranges = vec![]; @@ -212,7 +207,7 @@ impl<'a> ParLayouter<'a> { items.push(ParItem::Fractional(v)); ranges.push(range); } - ParChild::Text(_) => { + ParChild::Text(ref node) => { // TODO: Also split by language and script. let mut cursor = range.start; for (level, group) in bidi.levels[range].group_by_key(|&lvl| lvl) { @@ -220,7 +215,8 @@ impl<'a> ParLayouter<'a> { cursor += group.len(); let subrange = start .. cursor; let text = &bidi.text[subrange.clone()]; - let shaped = shape(ctx, text, styles, level.dir()); + let styles = node.styles.chain(&ctx.styles); + let shaped = shape(&mut ctx.fonts, text, styles, level.dir()); items.push(ParItem::Text(shaped)); ranges.push(subrange); } @@ -248,8 +244,8 @@ impl<'a> ParLayouter<'a> { } Self { - align: styles.get(ParNode::ALIGN), - leading: styles.get(ParNode::LEADING), + align: ctx.styles.get(ParNode::ALIGN), + leading: ctx.styles.get(ParNode::LEADING), bidi, items, ranges, @@ -426,7 +422,7 @@ impl<'a> LineLayout<'a> { // empty string. if !range.is_empty() || rest.is_empty() { // Reshape that part. - let reshaped = shaped.reshape(ctx, range); + let reshaped = shaped.reshape(&mut ctx.fonts, range); last = Some(ParItem::Text(reshaped)); } @@ -447,7 +443,7 @@ impl<'a> LineLayout<'a> { // Reshape if necessary. if range.len() < shaped.text.len() { if !range.is_empty() { - let reshaped = shaped.reshape(ctx, range); + let reshaped = shaped.reshape(&mut ctx.fonts, range); first = Some(ParItem::Text(reshaped)); } diff --git a/src/library/text.rs b/src/library/text.rs index 01218087..e8bb6093 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -53,7 +53,12 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { /// A single run of text with the same style. #[derive(Debug, Hash)] -pub struct TextNode(pub EcoString); +pub struct TextNode { + /// The run's text. + pub text: EcoString, + /// The run's styles. + pub styles: Styles, +} properties! { TextNode, @@ -138,12 +143,12 @@ pub enum FontFamily { impl Debug for FontFamily { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad(match self { - Self::Serif => "serif", - Self::SansSerif => "sans-serif", - Self::Monospace => "monospace", - Self::Named(s) => s, - }) + match self { + Self::Serif => f.pad("serif"), + Self::SansSerif => f.pad("sans-serif"), + Self::Monospace => f.pad("monospace"), + Self::Named(s) => s.fmt(f), + } } } @@ -329,28 +334,28 @@ castable! { /// Shape text into [`ShapedText`]. pub fn shape<'a>( - ctx: &mut LayoutContext, + fonts: &mut FontStore, text: &'a str, - styles: &'a Styles, + styles: Styles, dir: Dir, ) -> ShapedText<'a> { let mut glyphs = vec![]; if !text.is_empty() { shape_segment( - ctx.fonts, + fonts, &mut glyphs, 0, text, - variant(styles), - families(styles), + variant(&styles), + families(&styles), None, dir, - &tags(styles), + &tags(&styles), ); } track(&mut glyphs, styles.get(TextNode::TRACKING)); - let (size, baseline) = measure(ctx, &glyphs, styles); + let (size, baseline) = measure(fonts, &glyphs, &styles); ShapedText { text, @@ -507,7 +512,7 @@ fn track(glyphs: &mut [ShapedGlyph], tracking: Em) { /// Measure the size and baseline of a run of shaped glyphs with the given /// properties. fn measure( - ctx: &mut LayoutContext, + fonts: &mut FontStore, glyphs: &[ShapedGlyph], styles: &Styles, ) -> (Size, Length) { @@ -529,14 +534,14 @@ fn measure( // When there are no glyphs, we just use the vertical metrics of the // first available font. for family in families(styles) { - if let Some(face_id) = ctx.fonts.select(family, variant(styles)) { - expand(ctx.fonts.get(face_id)); + if let Some(face_id) = fonts.select(family, variant(styles)) { + expand(fonts.get(face_id)); break; } } } else { for (face_id, group) in glyphs.group_by_key(|g| g.face_id) { - let face = ctx.fonts.get(face_id); + let face = fonts.get(face_id); expand(face); for glyph in group { @@ -685,7 +690,8 @@ pub struct ShapedText<'a> { /// The text direction. pub dir: Dir, /// The text's style properties. - pub styles: &'a Styles, + // TODO(set): Go back to reference. + pub styles: Styles, /// The font size. pub size: Size, /// The baseline from the top of the frame. @@ -749,21 +755,21 @@ impl<'a> ShapedText<'a> { /// shaping process if possible. pub fn reshape( &'a self, - ctx: &mut LayoutContext, + fonts: &mut FontStore, text_range: Range<usize>, ) -> ShapedText<'a> { if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) { - let (size, baseline) = measure(ctx, glyphs, self.styles); + let (size, baseline) = measure(fonts, glyphs, &self.styles); Self { text: &self.text[text_range], dir: self.dir, - styles: self.styles, + styles: self.styles.clone(), size, baseline, glyphs: Cow::Borrowed(glyphs), } } else { - shape(ctx, &self.text[text_range], self.styles, self.dir) + shape(fonts, &self.text[text_range], self.styles.clone(), self.dir) } } |
