mirror of
https://github.com/typst/typst
synced 2025-08-12 14:17:55 +08:00
Fallible layout
This commit is contained in:
parent
c5e67af22b
commit
35610a8c6a
@ -4,12 +4,14 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{StyleChain, Template};
|
use super::{StyleChain, Template};
|
||||||
|
use crate::diag::TypResult;
|
||||||
use crate::util::Prehashed;
|
use crate::util::Prehashed;
|
||||||
|
use crate::Vm;
|
||||||
|
|
||||||
/// A node that can be realized given some styles.
|
/// A node that can be realized given some styles.
|
||||||
pub trait Show {
|
pub trait Show {
|
||||||
/// Realize the template in the given styles.
|
/// Realize the template in the given styles.
|
||||||
fn show(&self, styles: StyleChain) -> Template;
|
fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template>;
|
||||||
|
|
||||||
/// Convert to a packed show node.
|
/// Convert to a packed show node.
|
||||||
fn pack(self) -> ShowNode
|
fn pack(self) -> ShowNode
|
||||||
@ -40,8 +42,8 @@ impl ShowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for ShowNode {
|
impl Show for ShowNode {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
self.0.show(styles)
|
self.0.show(vm, styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pack(self) -> ShowNode {
|
fn pack(self) -> ShowNode {
|
||||||
|
@ -165,20 +165,22 @@ impl Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Layout this template into a collection of pages.
|
/// Layout this template into a collection of pages.
|
||||||
pub fn layout(&self, vm: &mut Vm) -> Vec<Arc<Frame>> {
|
pub fn layout(&self, vm: &mut Vm) -> TypResult<Vec<Arc<Frame>>> {
|
||||||
let style_arena = Arena::new();
|
let style_arena = Arena::new();
|
||||||
let template_arena = Arena::new();
|
let template_arena = Arena::new();
|
||||||
|
|
||||||
let mut builder = Builder::new(&style_arena, &template_arena, true);
|
let mut builder = Builder::new(&style_arena, &template_arena, true);
|
||||||
let chain = StyleChain::new(vm.styles);
|
let chain = StyleChain::new(vm.styles);
|
||||||
builder.process(self, chain);
|
builder.process(self, vm, chain)?;
|
||||||
builder.finish_page(true, false, chain);
|
builder.finish_page(true, false, chain);
|
||||||
|
|
||||||
|
let mut frames = vec![];
|
||||||
let (pages, shared) = builder.pages.unwrap().finish();
|
let (pages, shared) = builder.pages.unwrap().finish();
|
||||||
pages
|
for (page, map) in pages.iter() {
|
||||||
.iter()
|
frames.extend(page.layout(vm, map.chain(&shared))?);
|
||||||
.flat_map(|(page, map)| page.layout(vm, map.chain(&shared)))
|
}
|
||||||
.collect()
|
|
||||||
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,12 +271,12 @@ impl Layout for Template {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let style_arena = Arena::new();
|
let style_arena = Arena::new();
|
||||||
let template_arena = Arena::new();
|
let template_arena = Arena::new();
|
||||||
|
|
||||||
let mut builder = Builder::new(&style_arena, &template_arena, false);
|
let mut builder = Builder::new(&style_arena, &template_arena, false);
|
||||||
builder.process(self, styles);
|
builder.process(self, vm, styles)?;
|
||||||
builder.finish_par(styles);
|
builder.finish_par(styles);
|
||||||
|
|
||||||
let (flow, shared) = builder.flow.finish();
|
let (flow, shared) = builder.flow.finish();
|
||||||
@ -323,7 +325,12 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Process a template.
|
/// Process a template.
|
||||||
fn process(&mut self, template: &'a Template, styles: StyleChain<'a>) {
|
fn process(
|
||||||
|
&mut self,
|
||||||
|
template: &'a Template,
|
||||||
|
vm: &mut Vm,
|
||||||
|
styles: StyleChain<'a>,
|
||||||
|
) -> TypResult<()> {
|
||||||
match template {
|
match template {
|
||||||
Template::Space => {
|
Template::Space => {
|
||||||
self.par.weak(ParChild::Text(' '.into()), 0, styles);
|
self.par.weak(ParChild::Text(' '.into()), 0, styles);
|
||||||
@ -382,8 +389,9 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Template::Show(node) => {
|
Template::Show(node) => {
|
||||||
let template = self.template_arena.alloc(node.show(styles));
|
let template = node.show(vm, styles)?;
|
||||||
self.process(template, styles.unscoped(node.id()));
|
let stored = self.template_arena.alloc(template);
|
||||||
|
self.process(stored, vm, styles.unscoped(node.id()))?;
|
||||||
}
|
}
|
||||||
Template::Styled(styled) => {
|
Template::Styled(styled) => {
|
||||||
let (sub, map) = styled.as_ref();
|
let (sub, map) = styled.as_ref();
|
||||||
@ -397,7 +405,7 @@ impl<'a> Builder<'a> {
|
|||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.process(sub, styles);
|
self.process(sub, vm, styles)?;
|
||||||
|
|
||||||
match interruption {
|
match interruption {
|
||||||
Some(Interruption::Page) => self.finish_page(true, false, styles),
|
Some(Interruption::Page) => self.finish_page(true, false, styles),
|
||||||
@ -407,10 +415,12 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
Template::Sequence(seq) => {
|
Template::Sequence(seq) => {
|
||||||
for sub in seq.iter() {
|
for sub in seq.iter() {
|
||||||
self.process(sub, styles);
|
self.process(sub, vm, styles)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the currently built paragraph.
|
/// Finish the currently built paragraph.
|
||||||
|
@ -15,6 +15,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::diag::TypResult;
|
||||||
use crate::eval::StyleChain;
|
use crate::eval::StyleChain;
|
||||||
use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
|
use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
|
||||||
use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform};
|
use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform};
|
||||||
@ -33,7 +34,7 @@ pub trait Layout {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>>;
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>>;
|
||||||
|
|
||||||
/// Convert to a packed node.
|
/// Convert to a packed node.
|
||||||
fn pack(self) -> LayoutNode
|
fn pack(self) -> LayoutNode
|
||||||
@ -137,7 +138,7 @@ impl Layout for LayoutNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let styles = styles.barred(self.id());
|
let styles = styles.barred(self.id());
|
||||||
|
|
||||||
#[cfg(not(feature = "layout-cache"))]
|
#[cfg(not(feature = "layout-cache"))]
|
||||||
@ -155,10 +156,10 @@ impl Layout for LayoutNode {
|
|||||||
// #[track_caller] annotation doesn't work.
|
// #[track_caller] annotation doesn't work.
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
if let Some(frames) = vm.layout_cache.get(hash, regions) {
|
if let Some(frames) = vm.layout_cache.get(hash, regions) {
|
||||||
frames
|
Ok(frames)
|
||||||
} else {
|
} else {
|
||||||
vm.level += 1;
|
vm.level += 1;
|
||||||
let frames = self.0.layout(vm, regions, styles);
|
let frames = self.0.layout(vm, regions, styles)?;
|
||||||
vm.level -= 1;
|
vm.level -= 1;
|
||||||
|
|
||||||
let entry = FramesEntry::new(frames.clone(), vm.level);
|
let entry = FramesEntry::new(frames.clone(), vm.level);
|
||||||
@ -175,7 +176,7 @@ impl Layout for LayoutNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vm.layout_cache.insert(hash, entry);
|
vm.layout_cache.insert(hash, entry);
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,11 +249,11 @@ impl Layout for EmptyNode {
|
|||||||
_: &mut Vm,
|
_: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
_: StyleChain,
|
_: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let size = regions.expand.select(regions.current, Size::zero());
|
let size = regions.expand.select(regions.current, Size::zero());
|
||||||
let mut cts = Constraints::new(regions.expand);
|
let mut cts = Constraints::new(regions.expand);
|
||||||
cts.exact = regions.current.filter(regions.expand);
|
cts.exact = regions.current.filter(regions.expand);
|
||||||
vec![Frame::new(size).constrain(cts)]
|
Ok(vec![Frame::new(size).constrain(cts)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +272,7 @@ impl Layout for SizedNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let is_auto = self.sizing.map_is_none();
|
let is_auto = self.sizing.map_is_none();
|
||||||
let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative));
|
let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative));
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ impl Layout for SizedNode {
|
|||||||
Regions::one(size, base, expand)
|
Regions::one(size, base, expand)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut frames = self.child.layout(vm, &pod, styles);
|
let mut frames = self.child.layout(vm, &pod, styles)?;
|
||||||
let Constrained { item: frame, cts } = &mut frames[0];
|
let Constrained { item: frame, cts } = &mut frames[0];
|
||||||
|
|
||||||
// Ensure frame size matches regions size if expansion is on.
|
// Ensure frame size matches regions size if expansion is on.
|
||||||
@ -306,7 +307,7 @@ impl Layout for SizedNode {
|
|||||||
cts.exact = regions.current.filter(regions.expand | is_auto);
|
cts.exact = regions.current.filter(regions.expand | is_auto);
|
||||||
cts.base = regions.base.filter(is_rel | is_auto);
|
cts.base = regions.base.filter(is_rel | is_auto);
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,13 +326,13 @@ impl Layout for FillNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let mut frames = self.child.layout(vm, regions, styles);
|
let mut frames = self.child.layout(vm, regions, styles)?;
|
||||||
for Constrained { item: frame, .. } in &mut frames {
|
for Constrained { item: frame, .. } in &mut frames {
|
||||||
let shape = Shape::filled(Geometry::Rect(frame.size), self.fill);
|
let shape = Shape::filled(Geometry::Rect(frame.size), self.fill);
|
||||||
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
|
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
|
||||||
}
|
}
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,12 +351,12 @@ impl Layout for StrokeNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let mut frames = self.child.layout(vm, regions, styles);
|
let mut frames = self.child.layout(vm, regions, styles)?;
|
||||||
for Constrained { item: frame, .. } in &mut frames {
|
for Constrained { item: frame, .. } in &mut frames {
|
||||||
let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke);
|
let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke);
|
||||||
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
|
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
|
||||||
}
|
}
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,9 +283,7 @@ impl<'a> Vm<'a> {
|
|||||||
/// diagnostics in the form of a vector of error message with file and span
|
/// diagnostics in the form of a vector of error message with file and span
|
||||||
/// information.
|
/// information.
|
||||||
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
|
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
|
||||||
let module = self.evaluate(id)?;
|
self.evaluate(id)?.template.layout(self)
|
||||||
let frames = module.template.layout(self);
|
|
||||||
Ok(frames)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a user-entered path (relative to the source file) to be
|
/// Resolve a user-entered path (relative to the source file) to be
|
||||||
|
@ -27,7 +27,7 @@ impl Layout for AlignNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
// The child only needs to expand along an axis if there's no alignment.
|
// The child only needs to expand along an axis if there's no alignment.
|
||||||
let mut pod = regions.clone();
|
let mut pod = regions.clone();
|
||||||
pod.expand &= self.aligns.map_is_none();
|
pod.expand &= self.aligns.map_is_none();
|
||||||
@ -39,7 +39,7 @@ impl Layout for AlignNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout the child.
|
// Layout the child.
|
||||||
let mut frames = self.child.layout(vm, &pod, passed.chain(&styles));
|
let mut frames = self.child.layout(vm, &pod, passed.chain(&styles))?;
|
||||||
|
|
||||||
for ((current, base), Constrained { item: frame, cts }) in
|
for ((current, base), Constrained { item: frame, cts }) in
|
||||||
regions.iter().zip(&mut frames)
|
regions.iter().zip(&mut frames)
|
||||||
@ -57,7 +57,7 @@ impl Layout for AlignNode {
|
|||||||
cts.exact = current.filter(regions.expand | cts.exact.map_is_some());
|
cts.exact = current.filter(regions.expand | cts.exact.map_is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ impl Layout for ColumnsNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
// Separating the infinite space into infinite columns does not make
|
// Separating the infinite space into infinite columns does not make
|
||||||
// much sense.
|
// much sense.
|
||||||
if regions.current.x.is_infinite() {
|
if regions.current.x.is_infinite() {
|
||||||
@ -59,7 +59,7 @@ impl Layout for ColumnsNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Layout the children.
|
// Layout the children.
|
||||||
let mut frames = self.child.layout(vm, &pod, styles).into_iter();
|
let mut frames = self.child.layout(vm, &pod, styles)?.into_iter();
|
||||||
|
|
||||||
let dir = styles.get(ParNode::DIR);
|
let dir = styles.get(ParNode::DIR);
|
||||||
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
|
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
|
||||||
@ -102,7 +102,7 @@ impl Layout for ColumnsNode {
|
|||||||
finished.push(output.constrain(cts));
|
finished.push(output.constrain(cts));
|
||||||
}
|
}
|
||||||
|
|
||||||
finished
|
Ok(finished)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,15 +32,15 @@ impl<const L: DecoLine> DecoNode<L> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: DecoLine> Show for DecoNode<L> {
|
impl<const L: DecoLine> Show for DecoNode<L> {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
self.0.clone().styled(TextNode::LINES, vec![Decoration {
|
Ok(self.0.clone().styled(TextNode::LINES, vec![Decoration {
|
||||||
line: L,
|
line: L,
|
||||||
stroke: styles.get(Self::STROKE),
|
stroke: styles.get(Self::STROKE),
|
||||||
thickness: styles.get(Self::THICKNESS),
|
thickness: styles.get(Self::THICKNESS),
|
||||||
offset: styles.get(Self::OFFSET),
|
offset: styles.get(Self::OFFSET),
|
||||||
extent: styles.get(Self::EXTENT),
|
extent: styles.get(Self::EXTENT),
|
||||||
evade: styles.get(Self::EVADE),
|
evade: styles.get(Self::EVADE),
|
||||||
}])
|
}]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ impl Layout for FlowNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let mut layouter = FlowLayouter::new(regions);
|
let mut layouter = FlowLayouter::new(regions);
|
||||||
|
|
||||||
for (child, map) in self.0.iter() {
|
for (child, map) in self.0.iter() {
|
||||||
@ -56,12 +56,12 @@ impl Layout for FlowNode {
|
|||||||
layouter.layout_spacing(*kind);
|
layouter.layout_spacing(*kind);
|
||||||
}
|
}
|
||||||
FlowChild::Node(ref node) => {
|
FlowChild::Node(ref node) => {
|
||||||
layouter.layout_node(vm, node, styles);
|
layouter.layout_node(vm, node, styles)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layouter.finish()
|
Ok(layouter.finish())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +161,12 @@ impl FlowLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a node.
|
/// Layout a node.
|
||||||
pub fn layout_node(&mut self, vm: &mut Vm, node: &LayoutNode, styles: StyleChain) {
|
pub fn layout_node(
|
||||||
|
&mut self,
|
||||||
|
vm: &mut Vm,
|
||||||
|
node: &LayoutNode,
|
||||||
|
styles: StyleChain,
|
||||||
|
) -> TypResult<()> {
|
||||||
// Don't even try layouting into a full region.
|
// Don't even try layouting into a full region.
|
||||||
if self.regions.is_full() {
|
if self.regions.is_full() {
|
||||||
self.finish_region();
|
self.finish_region();
|
||||||
@ -171,9 +176,9 @@ impl FlowLayouter {
|
|||||||
// aligned later.
|
// aligned later.
|
||||||
if let Some(placed) = node.downcast::<PlaceNode>() {
|
if let Some(placed) = node.downcast::<PlaceNode>() {
|
||||||
if placed.out_of_flow() {
|
if placed.out_of_flow() {
|
||||||
let frame = node.layout(vm, &self.regions, styles).remove(0);
|
let frame = node.layout(vm, &self.regions, styles)?.remove(0);
|
||||||
self.items.push(FlowItem::Placed(frame.item));
|
self.items.push(FlowItem::Placed(frame.item));
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +193,7 @@ impl FlowLayouter {
|
|||||||
.unwrap_or(Align::Top),
|
.unwrap_or(Align::Top),
|
||||||
);
|
);
|
||||||
|
|
||||||
let frames = node.layout(vm, &self.regions, styles);
|
let frames = node.layout(vm, &self.regions, styles)?;
|
||||||
let len = frames.len();
|
let len = frames.len();
|
||||||
for (i, frame) in frames.into_iter().enumerate() {
|
for (i, frame) in frames.into_iter().enumerate() {
|
||||||
// Grow our size, shrink the region and save the frame for later.
|
// Grow our size, shrink the region and save the frame for later.
|
||||||
@ -202,6 +207,8 @@ impl FlowLayouter {
|
|||||||
self.finish_region();
|
self.finish_region();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the frame for one region.
|
/// Finish the frame for one region.
|
||||||
|
@ -38,7 +38,7 @@ impl Layout for GridNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
// Prepare grid layout by unifying content and gutter tracks.
|
// Prepare grid layout by unifying content and gutter tracks.
|
||||||
let layouter = GridLayouter::new(
|
let layouter = GridLayouter::new(
|
||||||
self.tracks.as_deref(),
|
self.tracks.as_deref(),
|
||||||
@ -48,7 +48,7 @@ impl Layout for GridNode {
|
|||||||
styles,
|
styles,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Measure the columsna nd layout the grid row-by-row.
|
// Measure the columns and layout the grid row-by-row.
|
||||||
layouter.layout(vm)
|
layouter.layout(vm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,19 +205,19 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the columns sizes and then layouts the grid row-by-row.
|
/// Determines the columns sizes and then layouts the grid row-by-row.
|
||||||
pub fn layout(mut self, vm: &mut Vm) -> Vec<Constrained<Arc<Frame>>> {
|
pub fn layout(mut self, vm: &mut Vm) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
self.measure_columns(vm);
|
self.measure_columns(vm)?;
|
||||||
|
|
||||||
for y in 0 .. self.rows.len() {
|
for y in 0 .. self.rows.len() {
|
||||||
// Skip to next region if current one is full, but only for content
|
// Skip to next region if current one is full, but only for content
|
||||||
// rows, not for gutter rows.
|
// rows, not for gutter rows.
|
||||||
if y % 2 == 0 && self.regions.is_full() {
|
if y % 2 == 0 && self.regions.is_full() {
|
||||||
self.finish_region(vm);
|
self.finish_region(vm)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.rows[y] {
|
match self.rows[y] {
|
||||||
TrackSizing::Auto => self.layout_auto_row(vm, y),
|
TrackSizing::Auto => self.layout_auto_row(vm, y)?,
|
||||||
TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y),
|
TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y)?,
|
||||||
TrackSizing::Fractional(v) => {
|
TrackSizing::Fractional(v) => {
|
||||||
self.cts.exact.y = Some(self.full);
|
self.cts.exact.y = Some(self.full);
|
||||||
self.lrows.push(Row::Fr(v, y));
|
self.lrows.push(Row::Fr(v, y));
|
||||||
@ -226,12 +226,12 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_region(vm);
|
self.finish_region(vm)?;
|
||||||
self.finished
|
Ok(self.finished)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine all column sizes.
|
/// Determine all column sizes.
|
||||||
fn measure_columns(&mut self, vm: &mut Vm) {
|
fn measure_columns(&mut self, vm: &mut Vm) -> TypResult<()> {
|
||||||
enum Case {
|
enum Case {
|
||||||
/// The column sizing is only determined by specified linear sizes.
|
/// The column sizing is only determined by specified linear sizes.
|
||||||
PurelyLinear,
|
PurelyLinear,
|
||||||
@ -277,7 +277,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let available = self.regions.current.x - linear;
|
let available = self.regions.current.x - linear;
|
||||||
if available >= Length::zero() {
|
if available >= Length::zero() {
|
||||||
// Determine size of auto columns.
|
// Determine size of auto columns.
|
||||||
let (auto, count) = self.measure_auto_columns(vm, available);
|
let (auto, count) = self.measure_auto_columns(vm, available)?;
|
||||||
|
|
||||||
// If there is remaining space, distribute it to fractional columns,
|
// If there is remaining space, distribute it to fractional columns,
|
||||||
// otherwise shrink auto columns.
|
// otherwise shrink auto columns.
|
||||||
@ -308,6 +308,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
// Sum up the resolved column sizes once here.
|
// Sum up the resolved column sizes once here.
|
||||||
self.used.x = self.rcols.iter().sum();
|
self.used.x = self.rcols.iter().sum();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Measure the size that is available to auto columns.
|
/// Measure the size that is available to auto columns.
|
||||||
@ -315,7 +317,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
available: Length,
|
available: Length,
|
||||||
) -> (Length, usize) {
|
) -> TypResult<(Length, usize)> {
|
||||||
let mut auto = Length::zero();
|
let mut auto = Length::zero();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
@ -340,7 +342,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pod.base.y = v.resolve(self.regions.base.y);
|
pod.base.y = v.resolve(self.regions.base.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = node.layout(vm, &pod, self.styles).remove(0).item;
|
let frame = node.layout(vm, &pod, self.styles)?.remove(0).item;
|
||||||
resolved.set_max(frame.size.x);
|
resolved.set_max(frame.size.x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +352,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
(auto, count)
|
Ok((auto, count))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Distribute remaining space to fractional columns.
|
/// Distribute remaining space to fractional columns.
|
||||||
@ -394,7 +396,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
/// Layout a row with automatic height. Such a row may break across multiple
|
/// Layout a row with automatic height. Such a row may break across multiple
|
||||||
/// regions.
|
/// regions.
|
||||||
fn layout_auto_row(&mut self, vm: &mut Vm, y: usize) {
|
fn layout_auto_row(&mut self, vm: &mut Vm, y: usize) -> TypResult<()> {
|
||||||
let mut resolved: Vec<Length> = vec![];
|
let mut resolved: Vec<Length> = vec![];
|
||||||
|
|
||||||
// Determine the size for each region of the row.
|
// Determine the size for each region of the row.
|
||||||
@ -409,7 +411,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut sizes = node
|
let mut sizes = node
|
||||||
.layout(vm, &pod, self.styles)
|
.layout(vm, &pod, self.styles)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|frame| frame.item.size.y);
|
.map(|frame| frame.item.size.y);
|
||||||
|
|
||||||
@ -427,14 +429,14 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
// Nothing to layout.
|
// Nothing to layout.
|
||||||
if resolved.is_empty() {
|
if resolved.is_empty() {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout into a single region.
|
// Layout into a single region.
|
||||||
if let &[first] = resolved.as_slice() {
|
if let &[first] = resolved.as_slice() {
|
||||||
let frame = self.layout_single_row(vm, first, y);
|
let frame = self.layout_single_row(vm, first, y)?;
|
||||||
self.push_row(frame);
|
self.push_row(frame);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand all but the last region if the space is not
|
// Expand all but the last region if the space is not
|
||||||
@ -449,36 +451,40 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout into multiple regions.
|
// Layout into multiple regions.
|
||||||
let frames = self.layout_multi_row(vm, &resolved, y);
|
let frames = self.layout_multi_row(vm, &resolved, y)?;
|
||||||
let len = frames.len();
|
let len = frames.len();
|
||||||
for (i, frame) in frames.into_iter().enumerate() {
|
for (i, frame) in frames.into_iter().enumerate() {
|
||||||
self.push_row(frame);
|
self.push_row(frame);
|
||||||
if i + 1 < len {
|
if i + 1 < len {
|
||||||
self.cts.exact.y = Some(self.full);
|
self.cts.exact.y = Some(self.full);
|
||||||
self.finish_region(vm);
|
self.finish_region(vm)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row with linear height. Such a row cannot break across multiple
|
/// Layout a row with linear height. Such a row cannot break across multiple
|
||||||
/// regions, but it may force a region break.
|
/// regions, but it may force a region break.
|
||||||
fn layout_linear_row(&mut self, vm: &mut Vm, v: Linear, y: usize) {
|
fn layout_linear_row(&mut self, vm: &mut Vm, v: Linear, y: usize) -> TypResult<()> {
|
||||||
let resolved = v.resolve(self.regions.base.y);
|
let resolved = v.resolve(self.regions.base.y);
|
||||||
let frame = self.layout_single_row(vm, resolved, y);
|
let frame = self.layout_single_row(vm, resolved, y)?;
|
||||||
|
|
||||||
// Skip to fitting region.
|
// Skip to fitting region.
|
||||||
let height = frame.size.y;
|
let height = frame.size.y;
|
||||||
while !self.regions.current.y.fits(height) && !self.regions.in_last() {
|
while !self.regions.current.y.fits(height) && !self.regions.in_last() {
|
||||||
self.cts.max.y = Some(self.used.y + height);
|
self.cts.max.y = Some(self.used.y + height);
|
||||||
self.finish_region(vm);
|
self.finish_region(vm)?;
|
||||||
|
|
||||||
// Don't skip multiple regions for gutter and don't push a row.
|
// Don't skip multiple regions for gutter and don't push a row.
|
||||||
if y % 2 == 1 {
|
if y % 2 == 1 {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push_row(frame);
|
self.push_row(frame);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row with fixed height and return its frame.
|
/// Layout a row with fixed height and return its frame.
|
||||||
@ -487,7 +493,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
height: Length,
|
height: Length,
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> Frame {
|
) -> TypResult<Frame> {
|
||||||
let mut output = Frame::new(Size::new(self.used.x, height));
|
let mut output = Frame::new(Size::new(self.used.x, height));
|
||||||
let mut pos = Point::zero();
|
let mut pos = Point::zero();
|
||||||
|
|
||||||
@ -502,14 +508,14 @@ impl<'a> GridLayouter<'a> {
|
|||||||
.select(self.regions.base, size);
|
.select(self.regions.base, size);
|
||||||
|
|
||||||
let pod = Regions::one(size, base, Spec::splat(true));
|
let pod = Regions::one(size, base, Spec::splat(true));
|
||||||
let frame = node.layout(vm, &pod, self.styles).remove(0);
|
let frame = node.layout(vm, &pod, self.styles)?.remove(0);
|
||||||
output.push_frame(pos, frame.item);
|
output.push_frame(pos, frame.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos.x += rcol;
|
pos.x += rcol;
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row spanning multiple regions.
|
/// Layout a row spanning multiple regions.
|
||||||
@ -518,7 +524,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
heights: &[Length],
|
heights: &[Length],
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> Vec<Frame> {
|
) -> TypResult<Vec<Frame>> {
|
||||||
// Prepare frames.
|
// Prepare frames.
|
||||||
let mut outputs: Vec<_> = heights
|
let mut outputs: Vec<_> = heights
|
||||||
.iter()
|
.iter()
|
||||||
@ -542,7 +548,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the layouted frames into the individual output frames.
|
// Push the layouted frames into the individual output frames.
|
||||||
let frames = node.layout(vm, &pod, self.styles);
|
let frames = node.layout(vm, &pod, self.styles)?;
|
||||||
for (output, frame) in outputs.iter_mut().zip(frames) {
|
for (output, frame) in outputs.iter_mut().zip(frames) {
|
||||||
output.push_frame(pos, frame.item);
|
output.push_frame(pos, frame.item);
|
||||||
}
|
}
|
||||||
@ -551,7 +557,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
pos.x += rcol;
|
pos.x += rcol;
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs
|
Ok(outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a row frame into the current region.
|
/// Push a row frame into the current region.
|
||||||
@ -562,7 +568,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finish rows for one region.
|
/// Finish rows for one region.
|
||||||
fn finish_region(&mut self, vm: &mut Vm) {
|
fn finish_region(&mut self, vm: &mut Vm) -> TypResult<()> {
|
||||||
// Determine the size of the grid in this region, expanding fully if
|
// Determine the size of the grid in this region, expanding fully if
|
||||||
// there are fr rows.
|
// there are fr rows.
|
||||||
let mut size = self.used;
|
let mut size = self.used;
|
||||||
@ -584,7 +590,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
Row::Fr(v, y) => {
|
Row::Fr(v, y) => {
|
||||||
let remaining = self.full - self.used.y;
|
let remaining = self.full - self.used.y;
|
||||||
let height = v.resolve(self.fr, remaining);
|
let height = v.resolve(self.fr, remaining);
|
||||||
self.layout_single_row(vm, height, y)
|
self.layout_single_row(vm, height, y)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -600,6 +606,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.used.y = Length::zero();
|
self.used.y = Length::zero();
|
||||||
self.fr = Fractional::zero();
|
self.fr = Fractional::zero();
|
||||||
self.cts = Constraints::new(self.expand);
|
self.cts = Constraints::new(self.expand);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the node in the cell in column `x` and row `y`.
|
/// Get the node in the cell in column `x` and row `y`.
|
||||||
|
@ -43,7 +43,7 @@ impl HeadingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for HeadingNode {
|
impl Show for HeadingNode {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
|
|
||||||
let upscale = (1.6 - 0.1 * self.level as f64).max(0.75);
|
let upscale = (1.6 - 0.1 * self.level as f64).max(0.75);
|
||||||
@ -93,6 +93,8 @@ impl Show for HeadingNode {
|
|||||||
seq.push(Template::Vertical(below.into()));
|
seq.push(Template::Vertical(below.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Template::block(Template::sequence(seq).styled_with_map(map))
|
Ok(Template::block(
|
||||||
|
Template::sequence(seq).styled_with_map(map),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,14 @@ impl Layout for HideNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let mut frames = self.0.layout(vm, regions, styles);
|
let mut frames = self.0.layout(vm, regions, styles)?;
|
||||||
|
|
||||||
// Clear the frames.
|
// Clear the frames.
|
||||||
for Constrained { item: frame, .. } in &mut frames {
|
for Constrained { item: frame, .. } in &mut frames {
|
||||||
*frame = Arc::new(Frame { elements: vec![], ..**frame });
|
*frame = Arc::new(Frame { elements: vec![], ..**frame });
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ impl Layout for ImageNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let img = vm.images.get(self.0);
|
let img = vm.images.get(self.0);
|
||||||
let pxw = img.width() as f64;
|
let pxw = img.width() as f64;
|
||||||
let pxh = img.height() as f64;
|
let pxh = img.height() as f64;
|
||||||
@ -91,7 +91,7 @@ impl Layout for ImageNode {
|
|||||||
frame.link(url);
|
frame.link(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![frame.constrain(Constraints::tight(regions))]
|
Ok(vec![frame.constrain(Constraints::tight(regions))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ impl LinkNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for LinkNode {
|
impl Show for LinkNode {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
map.set(TextNode::LINK, Some(self.url.clone()));
|
map.set(TextNode::LINK, Some(self.url.clone()));
|
||||||
|
|
||||||
@ -50,6 +50,6 @@ impl Show for LinkNode {
|
|||||||
body = body.underlined();
|
body = body.underlined();
|
||||||
}
|
}
|
||||||
|
|
||||||
body.styled_with_map(map)
|
Ok(body.styled_with_map(map))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ impl<const L: Labelling> ListNode<L> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: Labelling> Show for ListNode<L> {
|
impl<const L: Labelling> Show for ListNode<L> {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
let em = styles.get(TextNode::SIZE).abs;
|
let em = styles.get(TextNode::SIZE).abs;
|
||||||
let label_indent = styles.get(Self::LABEL_INDENT).resolve(em);
|
let label_indent = styles.get(Self::LABEL_INDENT).resolve(em);
|
||||||
let body_indent = styles.get(Self::BODY_INDENT).resolve(em);
|
let body_indent = styles.get(Self::BODY_INDENT).resolve(em);
|
||||||
@ -40,7 +40,7 @@ impl<const L: Labelling> Show for ListNode<L> {
|
|||||||
ORDERED | _ => format_eco!("{}.", self.number.unwrap_or(1)),
|
ORDERED | _ => format_eco!("{}.", self.number.unwrap_or(1)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Template::block(GridNode {
|
Ok(Template::block(GridNode {
|
||||||
tracks: Spec::with_x(vec![
|
tracks: Spec::with_x(vec![
|
||||||
TrackSizing::Linear(label_indent.into()),
|
TrackSizing::Linear(label_indent.into()),
|
||||||
TrackSizing::Auto,
|
TrackSizing::Auto,
|
||||||
@ -54,7 +54,7 @@ impl<const L: Labelling> Show for ListNode<L> {
|
|||||||
LayoutNode::default(),
|
LayoutNode::default(),
|
||||||
self.child.clone(),
|
self.child.clone(),
|
||||||
],
|
],
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ impl MathNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for MathNode {
|
impl Show for MathNode {
|
||||||
fn show(&self, _: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
|
||||||
let mut template = Template::Text(self.formula.trim().into());
|
let mut template = Template::Text(self.formula.trim().into());
|
||||||
if self.display {
|
if self.display {
|
||||||
template = Template::Block(template.pack());
|
template = Template::Block(template.pack());
|
||||||
}
|
}
|
||||||
template.monospaced()
|
Ok(template.monospaced())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,10 @@ impl Layout for PadNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
// Layout child into padded regions.
|
// Layout child into padded regions.
|
||||||
let pod = regions.map(|size| shrink(size, self.padding));
|
let pod = regions.map(|size| shrink(size, self.padding));
|
||||||
let mut frames = self.child.layout(vm, &pod, styles);
|
let mut frames = self.child.layout(vm, &pod, styles)?;
|
||||||
|
|
||||||
for ((current, base), Constrained { item: frame, cts }) in
|
for ((current, base), Constrained { item: frame, cts }) in
|
||||||
regions.iter().zip(&mut frames)
|
regions.iter().zip(&mut frames)
|
||||||
@ -70,7 +70,7 @@ impl Layout for PadNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ impl PageNode {
|
|||||||
|
|
||||||
impl PageNode {
|
impl PageNode {
|
||||||
/// Layout the page run into a sequence of frames, one per page.
|
/// Layout the page run into a sequence of frames, one per page.
|
||||||
pub fn layout(&self, vm: &mut Vm, styles: StyleChain) -> Vec<Arc<Frame>> {
|
pub fn layout(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Vec<Arc<Frame>>> {
|
||||||
// When one of the lengths is infinite the page fits its content along
|
// When one of the lengths is infinite the page fits its content along
|
||||||
// that axis.
|
// that axis.
|
||||||
let width = styles.get(Self::WIDTH).unwrap_or(Length::inf());
|
let width = styles.get(Self::WIDTH).unwrap_or(Length::inf());
|
||||||
@ -103,11 +103,11 @@ impl PageNode {
|
|||||||
// Layout the child.
|
// Layout the child.
|
||||||
let expand = size.map(Length::is_finite);
|
let expand = size.map(Length::is_finite);
|
||||||
let regions = Regions::repeat(size, size, expand);
|
let regions = Regions::repeat(size, size, expand);
|
||||||
child
|
Ok(child
|
||||||
.layout(vm, ®ions, styles)
|
.layout(vm, ®ions, styles)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| c.item)
|
.map(|c| c.item)
|
||||||
.collect()
|
.collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ impl Layout for ParNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
// Collect all text into one string used for BiDi analysis.
|
// Collect all text into one string used for BiDi analysis.
|
||||||
let text = self.collect_text();
|
let text = self.collect_text();
|
||||||
|
|
||||||
@ -95,10 +95,10 @@ impl Layout for ParNode {
|
|||||||
|
|
||||||
// Prepare paragraph layout by building a representation on which we can
|
// Prepare paragraph layout by building a representation on which we can
|
||||||
// do line breaking without layouting each and every line from scratch.
|
// do line breaking without layouting each and every line from scratch.
|
||||||
let layouter = ParLayouter::new(self, vm, regions, &styles, bidi);
|
let layouter = ParLayouter::new(self, vm, regions, &styles, bidi)?;
|
||||||
|
|
||||||
// Find suitable linebreaks.
|
// Find suitable linebreaks.
|
||||||
layouter.layout(vm, regions.clone())
|
Ok(layouter.layout(vm, regions.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ impl<'a> ParLayouter<'a> {
|
|||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: &'a StyleChain<'a>,
|
styles: &'a StyleChain<'a>,
|
||||||
bidi: BidiInfo<'a>,
|
bidi: BidiInfo<'a>,
|
||||||
) -> Self {
|
) -> TypResult<Self> {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
let mut ranges = vec![];
|
let mut ranges = vec![];
|
||||||
|
|
||||||
@ -255,7 +255,7 @@ impl<'a> ParLayouter<'a> {
|
|||||||
ParChild::Node(node) => {
|
ParChild::Node(node) => {
|
||||||
let size = Size::new(regions.current.x, regions.base.y);
|
let size = Size::new(regions.current.x, regions.base.y);
|
||||||
let pod = Regions::one(size, regions.base, Spec::splat(false));
|
let pod = Regions::one(size, regions.base, Spec::splat(false));
|
||||||
let frame = node.layout(vm, &pod, styles).remove(0);
|
let frame = node.layout(vm, &pod, styles)?.remove(0);
|
||||||
items.push(ParItem::Frame(Arc::take(frame.item)));
|
items.push(ParItem::Frame(Arc::take(frame.item)));
|
||||||
ranges.push(range);
|
ranges.push(range);
|
||||||
}
|
}
|
||||||
@ -266,7 +266,7 @@ impl<'a> ParLayouter<'a> {
|
|||||||
let align = styles.get(ParNode::ALIGN);
|
let align = styles.get(ParNode::ALIGN);
|
||||||
let leading = styles.get(ParNode::LEADING).resolve(em);
|
let leading = styles.get(ParNode::LEADING).resolve(em);
|
||||||
|
|
||||||
Self { align, leading, bidi, items, ranges }
|
Ok(Self { align, leading, bidi, items, ranges })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find first-fit line breaks and build the paragraph.
|
/// Find first-fit line breaks and build the paragraph.
|
||||||
|
@ -26,7 +26,7 @@ impl Layout for PlaceNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let out_of_flow = self.out_of_flow();
|
let out_of_flow = self.out_of_flow();
|
||||||
|
|
||||||
// The pod is the base area of the region because for absolute
|
// The pod is the base area of the region because for absolute
|
||||||
@ -37,7 +37,7 @@ impl Layout for PlaceNode {
|
|||||||
Regions::one(regions.base, regions.base, expand)
|
Regions::one(regions.base, regions.base, expand)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut frames = self.0.layout(vm, &pod, styles);
|
let mut frames = self.0.layout(vm, &pod, styles)?;
|
||||||
let Constrained { item: frame, cts } = &mut frames[0];
|
let Constrained { item: frame, cts } = &mut frames[0];
|
||||||
|
|
||||||
// If expansion is off, zero all sizes so that we don't take up any
|
// If expansion is off, zero all sizes so that we don't take up any
|
||||||
@ -51,7 +51,7 @@ impl Layout for PlaceNode {
|
|||||||
cts.base = regions.base.map(Some);
|
cts.base = regions.base.map(Some);
|
||||||
cts.exact = regions.current.filter(regions.expand | out_of_flow);
|
cts.exact = regions.current.filter(regions.expand | out_of_flow);
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ impl RawNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for RawNode {
|
impl Show for RawNode {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
let lang = styles.get_ref(Self::LANG).as_ref();
|
let lang = styles.get_ref(Self::LANG).as_ref();
|
||||||
let foreground = THEME
|
let foreground = THEME
|
||||||
.settings
|
.settings
|
||||||
@ -87,7 +87,7 @@ impl Show for RawNode {
|
|||||||
template = Template::Block(template.pack());
|
template = Template::Block(template.pack());
|
||||||
}
|
}
|
||||||
|
|
||||||
template.monospaced()
|
Ok(template.monospaced())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let mut frames;
|
let mut frames;
|
||||||
if let Some(child) = &self.0 {
|
if let Some(child) = &self.0 {
|
||||||
let mut padding = styles.get(Self::PADDING);
|
let mut padding = styles.get(Self::PADDING);
|
||||||
@ -61,7 +61,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
let child = child.clone().padded(Sides::splat(padding));
|
let child = child.clone().padded(Sides::splat(padding));
|
||||||
|
|
||||||
let mut pod = Regions::one(regions.current, regions.base, regions.expand);
|
let mut pod = Regions::one(regions.current, regions.base, regions.expand);
|
||||||
frames = child.layout(vm, &pod, styles);
|
frames = child.layout(vm, &pod, styles)?;
|
||||||
|
|
||||||
// Relayout with full expansion into square region to make sure
|
// Relayout with full expansion into square region to make sure
|
||||||
// the result is really a square or circle.
|
// the result is really a square or circle.
|
||||||
@ -77,7 +77,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
|
|
||||||
pod.current = Size::splat(length);
|
pod.current = Size::splat(length);
|
||||||
pod.expand = Spec::splat(true);
|
pod.expand = Spec::splat(true);
|
||||||
frames = child.layout(vm, &pod, styles);
|
frames = child.layout(vm, &pod, styles)?;
|
||||||
frames[0].cts = Constraints::tight(regions);
|
frames[0].cts = Constraints::tight(regions);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -127,7 +127,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
frame.link(url);
|
frame.link(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ impl Layout for StackNode {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let mut layouter = StackLayouter::new(self.dir, regions);
|
let mut layouter = StackLayouter::new(self.dir, regions);
|
||||||
|
|
||||||
// Spacing to insert before the next node.
|
// Spacing to insert before the next node.
|
||||||
@ -48,13 +48,13 @@ impl Layout for StackNode {
|
|||||||
layouter.layout_spacing(kind);
|
layouter.layout_spacing(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
layouter.layout_node(vm, node, styles);
|
layouter.layout_node(vm, node, styles)?;
|
||||||
deferred = self.spacing;
|
deferred = self.spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layouter.finish()
|
Ok(layouter.finish())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +163,12 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Layout an arbitrary node.
|
/// Layout an arbitrary node.
|
||||||
pub fn layout_node(&mut self, vm: &mut Vm, node: &LayoutNode, styles: StyleChain) {
|
pub fn layout_node(
|
||||||
|
&mut self,
|
||||||
|
vm: &mut Vm,
|
||||||
|
node: &LayoutNode,
|
||||||
|
styles: StyleChain,
|
||||||
|
) -> TypResult<()> {
|
||||||
if self.regions.is_full() {
|
if self.regions.is_full() {
|
||||||
self.finish_region();
|
self.finish_region();
|
||||||
}
|
}
|
||||||
@ -174,7 +179,7 @@ impl StackLayouter {
|
|||||||
.and_then(|node| node.aligns.get(self.axis))
|
.and_then(|node| node.aligns.get(self.axis))
|
||||||
.unwrap_or(self.dir.start().into());
|
.unwrap_or(self.dir.start().into());
|
||||||
|
|
||||||
let frames = node.layout(vm, &self.regions, styles);
|
let frames = node.layout(vm, &self.regions, styles)?;
|
||||||
let len = frames.len();
|
let len = frames.len();
|
||||||
for (i, frame) in frames.into_iter().enumerate() {
|
for (i, frame) in frames.into_iter().enumerate() {
|
||||||
// Grow our size, shrink the region and save the frame for later.
|
// Grow our size, shrink the region and save the frame for later.
|
||||||
@ -188,6 +193,8 @@ impl StackLayouter {
|
|||||||
self.finish_region();
|
self.finish_region();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance to the next region.
|
/// Advance to the next region.
|
||||||
|
@ -55,7 +55,7 @@ impl TableNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for TableNode {
|
impl Show for TableNode {
|
||||||
fn show(&self, styles: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
|
||||||
let primary = styles.get(Self::PRIMARY);
|
let primary = styles.get(Self::PRIMARY);
|
||||||
let secondary = styles.get(Self::SECONDARY);
|
let secondary = styles.get(Self::SECONDARY);
|
||||||
let thickness = styles.get(Self::THICKNESS);
|
let thickness = styles.get(Self::THICKNESS);
|
||||||
@ -85,10 +85,10 @@ impl Show for TableNode {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Template::block(GridNode {
|
Ok(Template::block(GridNode {
|
||||||
tracks: self.tracks.clone(),
|
tracks: self.tracks.clone(),
|
||||||
gutter: self.gutter.clone(),
|
gutter: self.gutter.clone(),
|
||||||
children,
|
children,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,8 +123,8 @@ impl StrongNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for StrongNode {
|
impl Show for StrongNode {
|
||||||
fn show(&self, _: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
|
||||||
self.0.clone().styled(TextNode::STRONG, true)
|
Ok(self.0.clone().styled(TextNode::STRONG, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,8 +140,8 @@ impl EmphNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for EmphNode {
|
impl Show for EmphNode {
|
||||||
fn show(&self, _: StyleChain) -> Template {
|
fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
|
||||||
self.0.clone().styled(TextNode::EMPH, true)
|
Ok(self.0.clone().styled(TextNode::EMPH, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,9 +49,9 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
|
|||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Vec<Constrained<Arc<Frame>>> {
|
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
|
||||||
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
|
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
|
||||||
let mut frames = self.child.layout(vm, regions, styles);
|
let mut frames = self.child.layout(vm, regions, styles)?;
|
||||||
|
|
||||||
for Constrained { item: frame, .. } in &mut frames {
|
for Constrained { item: frame, .. } in &mut frames {
|
||||||
let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s));
|
let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s));
|
||||||
@ -62,7 +62,7 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
|
|||||||
Arc::make_mut(frame).transform(transform);
|
Arc::make_mut(frame).transform(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
Ok(frames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,14 +275,13 @@ fn test_part(
|
|||||||
ok &= test_reparse(ctx.sources.get(id).src(), i, rng);
|
ok &= test_reparse(ctx.sources.get(id).src(), i, rng);
|
||||||
|
|
||||||
let mut vm = Vm::new(ctx);
|
let mut vm = Vm::new(ctx);
|
||||||
let (frames, mut errors) = match vm.evaluate(id) {
|
let (frames, mut errors) = match vm.typeset(id) {
|
||||||
Ok(module) => {
|
Ok(mut frames) => {
|
||||||
|
let module = vm.evaluate(id).unwrap();
|
||||||
if debug {
|
if debug {
|
||||||
println!("Template: {:#?}", module.template);
|
println!("Template: {:#?}", module.template);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut frames = module.template.layout(&mut vm);
|
|
||||||
|
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
(ok &= test_incremental(ctx, i, &module.template, &frames));
|
(ok &= test_incremental(ctx, i, &module.template, &frames));
|
||||||
|
|
||||||
@ -499,7 +498,7 @@ fn test_incremental(
|
|||||||
|
|
||||||
ctx.layout_cache.turnaround();
|
ctx.layout_cache.turnaround();
|
||||||
|
|
||||||
let cached = silenced(|| template.layout(&mut Vm::new(ctx)));
|
let cached = silenced(|| template.layout(&mut Vm::new(ctx)).unwrap());
|
||||||
let total = reference.levels() - 1;
|
let total = reference.levels() - 1;
|
||||||
let misses = ctx
|
let misses = ctx
|
||||||
.layout_cache
|
.layout_cache
|
||||||
|
Loading…
x
Reference in New Issue
Block a user