mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Better expansion behaviour 🐪
This makes expansion behaviour inheritable by placing it into the area and passing it down during layouting instead of computing some approximation of what we want during execution.
This commit is contained in:
parent
4e5f85aa4a
commit
c1b1dbcc09
@ -7,7 +7,7 @@ use super::*;
|
|||||||
use crate::diag::{Diag, DiagSet};
|
use crate::diag::{Diag, DiagSet};
|
||||||
use crate::geom::{ChildAlign, Dir, Gen, LayoutDirs, Length, Linear, Sides, Size};
|
use crate::geom::{ChildAlign, Dir, Gen, LayoutDirs, Length, Linear, Sides, Size};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Expansion, Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
|
Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
|
||||||
};
|
};
|
||||||
use crate::parse::is_newline;
|
use crate::parse::is_newline;
|
||||||
|
|
||||||
@ -105,18 +105,18 @@ impl<'a> ExecContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the body of a function and return the result as a stack node.
|
/// Execute a template and return the result as a stack node.
|
||||||
pub fn exec_body(&mut self, body: &ValueTemplate, expand: Spec<Expansion>) -> Node {
|
pub fn exec(&mut self, template: &ValueTemplate) -> Node {
|
||||||
let dirs = self.state.dirs;
|
let dirs = self.state.dirs;
|
||||||
let align = self.state.align;
|
let align = self.state.align;
|
||||||
|
|
||||||
self.start_group(ContentGroup);
|
self.start_group(ContentGroup);
|
||||||
self.start_par_group();
|
self.start_par_group();
|
||||||
body.exec(self);
|
template.exec(self);
|
||||||
self.end_par_group();
|
self.end_par_group();
|
||||||
let children = self.end_group::<ContentGroup>().1;
|
let children = self.end_group::<ContentGroup>().1;
|
||||||
|
|
||||||
NodeStack { dirs, align, expand, children }.into()
|
NodeStack { dirs, align, children }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a page group based on the active page state.
|
/// Start a page group based on the active page state.
|
||||||
@ -128,7 +128,6 @@ impl<'a> ExecContext<'a> {
|
|||||||
pub fn start_page_group(&mut self, softness: Softness) {
|
pub fn start_page_group(&mut self, softness: Softness) {
|
||||||
self.start_group(PageGroup {
|
self.start_group(PageGroup {
|
||||||
size: self.state.page.size,
|
size: self.state.page.size,
|
||||||
expand: self.state.page.expand,
|
|
||||||
padding: self.state.page.margins(),
|
padding: self.state.page.margins(),
|
||||||
dirs: self.state.dirs,
|
dirs: self.state.dirs,
|
||||||
align: self.state.align,
|
align: self.state.align,
|
||||||
@ -158,7 +157,6 @@ impl<'a> ExecContext<'a> {
|
|||||||
child: NodeStack {
|
child: NodeStack {
|
||||||
dirs: group.dirs,
|
dirs: group.dirs,
|
||||||
align: group.align,
|
align: group.align,
|
||||||
expand: group.expand,
|
|
||||||
children,
|
children,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
@ -186,13 +184,6 @@ impl<'a> ExecContext<'a> {
|
|||||||
self.push(NodePar {
|
self.push(NodePar {
|
||||||
dirs: group.dirs,
|
dirs: group.dirs,
|
||||||
align: group.align,
|
align: group.align,
|
||||||
// FIXME: This is a hack and should be superseded by something
|
|
||||||
// better.
|
|
||||||
cross_expansion: if self.groups.len() <= 1 {
|
|
||||||
Expansion::Fill
|
|
||||||
} else {
|
|
||||||
Expansion::Fit
|
|
||||||
},
|
|
||||||
line_spacing: group.line_spacing,
|
line_spacing: group.line_spacing,
|
||||||
children,
|
children,
|
||||||
});
|
});
|
||||||
@ -306,7 +297,6 @@ pub enum Softness {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PageGroup {
|
struct PageGroup {
|
||||||
size: Size,
|
size: Size,
|
||||||
expand: Spec<Expansion>,
|
|
||||||
padding: Sides<Linear>,
|
padding: Sides<Linear>,
|
||||||
dirs: LayoutDirs,
|
dirs: LayoutDirs,
|
||||||
align: ChildAlign,
|
align: ChildAlign,
|
||||||
|
@ -11,8 +11,7 @@ use std::rc::Rc;
|
|||||||
use crate::diag::Pass;
|
use crate::diag::Pass;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::eval::{ExprMap, TemplateFunc, TemplateNode, Value, ValueTemplate};
|
use crate::eval::{ExprMap, TemplateFunc, TemplateNode, Value, ValueTemplate};
|
||||||
use crate::geom::Spec;
|
use crate::layout::{self, NodeFixed, NodeSpacing, NodeStack};
|
||||||
use crate::layout::{self, Expansion, NodeSpacing, NodeStack};
|
|
||||||
use crate::pretty::pretty;
|
use crate::pretty::pretty;
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
|
|
||||||
@ -120,11 +119,17 @@ impl Exec for NodeRaw {
|
|||||||
ctx.apply_parbreak();
|
ctx.apply_parbreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.push(NodeStack {
|
// This is wrapped in a fixed node to make sure the stack fits to its
|
||||||
dirs: ctx.state.dirs,
|
// content instead of filling the available area.
|
||||||
align: ctx.state.align,
|
ctx.push(NodeFixed {
|
||||||
expand: Spec::uniform(Expansion::Fit),
|
width: None,
|
||||||
children,
|
height: None,
|
||||||
|
child: NodeStack {
|
||||||
|
dirs: ctx.state.dirs,
|
||||||
|
align: ctx.state.align,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.block {
|
if self.block {
|
||||||
|
@ -3,9 +3,8 @@ use std::rc::Rc;
|
|||||||
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
|
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
|
||||||
|
|
||||||
use crate::geom::{
|
use crate::geom::{
|
||||||
Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size, Spec,
|
Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size,
|
||||||
};
|
};
|
||||||
use crate::layout::Expansion;
|
|
||||||
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
||||||
|
|
||||||
/// The evaluation state.
|
/// The evaluation state.
|
||||||
@ -42,8 +41,6 @@ pub struct PageState {
|
|||||||
pub class: PaperClass,
|
pub class: PaperClass,
|
||||||
/// The width and height of the page.
|
/// The width and height of the page.
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
/// Whether the expand the pages to the `size` or to fit the content.
|
|
||||||
pub expand: Spec<Expansion>,
|
|
||||||
/// The amount of white space on each side of the page. If a side is set to
|
/// The amount of white space on each side of the page. If a side is set to
|
||||||
/// `None`, the default for the paper class is used.
|
/// `None`, the default for the paper class is used.
|
||||||
pub margins: Sides<Option<Linear>>,
|
pub margins: Sides<Option<Linear>>,
|
||||||
@ -55,7 +52,6 @@ impl PageState {
|
|||||||
Self {
|
Self {
|
||||||
class: paper.class,
|
class: paper.class,
|
||||||
size: paper.size(),
|
size: paper.size(),
|
||||||
expand: Spec::uniform(Expansion::Fill),
|
|
||||||
margins: Sides::uniform(None),
|
margins: Sides::uniform(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,13 @@ impl Layout for NodeFixed {
|
|||||||
self.height.map(|h| h.resolve(full.height)).unwrap_or(current.height),
|
self.height.map(|h| h.resolve(full.height)).unwrap_or(current.height),
|
||||||
);
|
);
|
||||||
|
|
||||||
let areas = Areas::once(size);
|
let fill_if = |cond| if cond { Expand::Fill } else { Expand::Fit };
|
||||||
|
let expand = Spec::new(
|
||||||
|
fill_if(self.width.is_some()),
|
||||||
|
fill_if(self.height.is_some()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let areas = Areas::once(size, expand);
|
||||||
self.child.layout(ctx, &areas)
|
self.child.layout(ctx, &areas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ pub struct NodePages {
|
|||||||
impl NodePages {
|
impl NodePages {
|
||||||
/// Layout the page run.
|
/// Layout the page run.
|
||||||
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
|
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
|
||||||
let areas = Areas::repeat(self.size);
|
let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill));
|
||||||
let layouted = self.child.layout(ctx, &areas);
|
let layouted = self.child.layout(ctx, &areas);
|
||||||
layouted.into_frames()
|
layouted.into_frames()
|
||||||
}
|
}
|
||||||
@ -85,26 +85,31 @@ pub struct Areas {
|
|||||||
pub backlog: Vec<Size>,
|
pub backlog: Vec<Size>,
|
||||||
/// The final area that is repeated when the backlog is empty.
|
/// The final area that is repeated when the backlog is empty.
|
||||||
pub last: Option<Size>,
|
pub last: Option<Size>,
|
||||||
|
/// Whether the frames resulting from layouting into this areas should be
|
||||||
|
/// shrunk to fit their content or expanded to fill the area.
|
||||||
|
pub expand: Spec<Expand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Areas {
|
impl Areas {
|
||||||
/// Create a new length-1 sequence of areas with just one `area`.
|
/// Create a new length-1 sequence of areas with just one `area`.
|
||||||
pub fn once(size: Size) -> Self {
|
pub fn once(size: Size, expand: Spec<Expand>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
current: size,
|
current: size,
|
||||||
full: size,
|
full: size,
|
||||||
backlog: vec![],
|
backlog: vec![],
|
||||||
last: None,
|
last: None,
|
||||||
|
expand,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new sequence of areas that repeats `area` indefinitely.
|
/// Create a new sequence of areas that repeats `area` indefinitely.
|
||||||
pub fn repeat(size: Size) -> Self {
|
pub fn repeat(size: Size, expand: Spec<Expand>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
current: size,
|
current: size,
|
||||||
full: size,
|
full: size,
|
||||||
backlog: vec![],
|
backlog: vec![],
|
||||||
last: Some(size),
|
last: Some(size),
|
||||||
|
expand,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,14 +134,14 @@ impl Areas {
|
|||||||
|
|
||||||
/// Whether to expand or shrink a node along an axis.
|
/// Whether to expand or shrink a node along an axis.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum Expansion {
|
pub enum Expand {
|
||||||
/// Fit the content.
|
/// Fit the content.
|
||||||
Fit,
|
Fit,
|
||||||
/// Fill the available space.
|
/// Fill the available space.
|
||||||
Fill,
|
Fill,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expansion {
|
impl Expand {
|
||||||
/// Resolve the expansion to either the `fit` or `fill` length.
|
/// Resolve the expansion to either the `fit` or `fill` length.
|
||||||
///
|
///
|
||||||
/// Prefers `fit` if `fill` is infinite.
|
/// Prefers `fit` if `fill` is infinite.
|
||||||
|
@ -37,6 +37,7 @@ fn shrink(areas: &Areas, padding: Sides<Linear>) -> Areas {
|
|||||||
full: shrink(areas.full),
|
full: shrink(areas.full),
|
||||||
backlog: areas.backlog.iter().copied().map(shrink).collect(),
|
backlog: areas.backlog.iter().copied().map(shrink).collect(),
|
||||||
last: areas.last.map(shrink),
|
last: areas.last.map(shrink),
|
||||||
|
expand: areas.expand,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,6 @@ pub struct NodePar {
|
|||||||
/// The children are placed in lines along the `cross` direction. The lines
|
/// The children are placed in lines along the `cross` direction. The lines
|
||||||
/// are stacked along the `main` direction.
|
/// are stacked along the `main` direction.
|
||||||
pub dirs: LayoutDirs,
|
pub dirs: LayoutDirs,
|
||||||
/// Whether to expand the cross axis to fill the area or to fit the content.
|
|
||||||
pub cross_expansion: Expansion,
|
|
||||||
/// The spacing to insert after each line.
|
/// The spacing to insert after each line.
|
||||||
pub line_spacing: Length,
|
pub line_spacing: Length,
|
||||||
/// The nodes to be arranged in a paragraph.
|
/// The nodes to be arranged in a paragraph.
|
||||||
@ -42,11 +40,11 @@ impl From<NodePar> for NodeAny {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParLayouter<'a> {
|
struct ParLayouter {
|
||||||
par: &'a NodePar,
|
|
||||||
main: SpecAxis,
|
main: SpecAxis,
|
||||||
cross: SpecAxis,
|
cross: SpecAxis,
|
||||||
dirs: LayoutDirs,
|
dirs: LayoutDirs,
|
||||||
|
line_spacing: Length,
|
||||||
areas: Areas,
|
areas: Areas,
|
||||||
finished: Vec<Frame>,
|
finished: Vec<Frame>,
|
||||||
lines: Vec<(Length, Frame, Align)>,
|
lines: Vec<(Length, Frame, Align)>,
|
||||||
@ -56,13 +54,13 @@ struct ParLayouter<'a> {
|
|||||||
line_ruler: Align,
|
line_ruler: Align,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ParLayouter<'a> {
|
impl ParLayouter {
|
||||||
fn new(par: &'a NodePar, areas: Areas) -> Self {
|
fn new(par: &NodePar, areas: Areas) -> Self {
|
||||||
Self {
|
Self {
|
||||||
par,
|
|
||||||
main: par.dirs.main.axis(),
|
main: par.dirs.main.axis(),
|
||||||
cross: par.dirs.cross.axis(),
|
cross: par.dirs.cross.axis(),
|
||||||
dirs: par.dirs,
|
dirs: par.dirs,
|
||||||
|
line_spacing: par.line_spacing,
|
||||||
areas,
|
areas,
|
||||||
finished: vec![],
|
finished: vec![],
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
@ -134,13 +132,12 @@ impl<'a> ParLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn finish_line(&mut self) {
|
fn finish_line(&mut self) {
|
||||||
|
let expand = self.areas.expand.switch(self.dirs);
|
||||||
let full_size = {
|
let full_size = {
|
||||||
let full = self.areas.full.switch(self.dirs);
|
let full = self.areas.full.switch(self.dirs);
|
||||||
Gen::new(
|
Gen::new(
|
||||||
self.line_size.main,
|
self.line_size.main,
|
||||||
self.par
|
expand.cross.resolve(self.line_size.cross.min(full.cross), full.cross),
|
||||||
.cross_expansion
|
|
||||||
.resolve(self.line_size.cross.min(full.cross), full.cross),
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -165,8 +162,8 @@ impl<'a> ParLayouter<'a> {
|
|||||||
|
|
||||||
// Add line spacing, but only between lines.
|
// Add line spacing, but only between lines.
|
||||||
if !self.lines.is_empty() {
|
if !self.lines.is_empty() {
|
||||||
self.lines_size.main += self.par.line_spacing;
|
self.lines_size.main += self.line_spacing;
|
||||||
*self.areas.current.get_mut(self.main) -= self.par.line_spacing;
|
*self.areas.current.get_mut(self.main) -= self.line_spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update metrics of the whole paragraph.
|
// Update metrics of the whole paragraph.
|
||||||
|
@ -10,8 +10,6 @@ pub struct NodeStack {
|
|||||||
pub dirs: LayoutDirs,
|
pub dirs: LayoutDirs,
|
||||||
/// How to align this stack in _its_ parent.
|
/// How to align this stack in _its_ parent.
|
||||||
pub align: ChildAlign,
|
pub align: ChildAlign,
|
||||||
/// Whether to expand the axes to fill the area or to fit the content.
|
|
||||||
pub expand: Spec<Expansion>,
|
|
||||||
/// The nodes to be stacked.
|
/// The nodes to be stacked.
|
||||||
pub children: Vec<Node>,
|
pub children: Vec<Node>,
|
||||||
}
|
}
|
||||||
@ -40,8 +38,7 @@ impl From<NodeStack> for NodeAny {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StackLayouter<'a> {
|
struct StackLayouter {
|
||||||
stack: &'a NodeStack,
|
|
||||||
main: SpecAxis,
|
main: SpecAxis,
|
||||||
dirs: LayoutDirs,
|
dirs: LayoutDirs,
|
||||||
areas: Areas,
|
areas: Areas,
|
||||||
@ -51,10 +48,9 @@ struct StackLayouter<'a> {
|
|||||||
ruler: Align,
|
ruler: Align,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StackLayouter<'a> {
|
impl StackLayouter {
|
||||||
fn new(stack: &'a NodeStack, areas: Areas) -> Self {
|
fn new(stack: &NodeStack, areas: Areas) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack,
|
|
||||||
main: stack.dirs.main.axis(),
|
main: stack.dirs.main.axis(),
|
||||||
dirs: stack.dirs,
|
dirs: stack.dirs,
|
||||||
areas,
|
areas,
|
||||||
@ -97,7 +93,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
|
|
||||||
fn finish_area(&mut self) {
|
fn finish_area(&mut self) {
|
||||||
let full_size = {
|
let full_size = {
|
||||||
let expand = self.stack.expand.switch(self.dirs);
|
let expand = self.areas.expand.switch(self.dirs);
|
||||||
let full = self.areas.full.switch(self.dirs);
|
let full = self.areas.full.switch(self.dirs);
|
||||||
Gen::new(
|
Gen::new(
|
||||||
expand.main.resolve(self.used.main.min(full.main), full.main),
|
expand.main.resolve(self.used.main.min(full.main), full.main),
|
||||||
|
@ -29,8 +29,7 @@ pub fn pad(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
|
|||||||
Value::template("pad", move |ctx| {
|
Value::template("pad", move |ctx| {
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
|
|
||||||
let expand = Spec::uniform(Expansion::Fit);
|
let child = ctx.exec(&body);
|
||||||
let child = ctx.exec_body(&body, expand);
|
|
||||||
ctx.push(NodePad { padding, child });
|
ctx.push(NodePad { padding, child });
|
||||||
|
|
||||||
ctx.state = snapshot;
|
ctx.state = snapshot;
|
||||||
|
@ -45,19 +45,16 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
|
|||||||
if let Some(paper) = paper {
|
if let Some(paper) = paper {
|
||||||
ctx.state.page.class = paper.class;
|
ctx.state.page.class = paper.class;
|
||||||
ctx.state.page.size = paper.size();
|
ctx.state.page.size = paper.size();
|
||||||
ctx.state.page.expand = Spec::uniform(Expansion::Fill);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(width) = width {
|
if let Some(width) = width {
|
||||||
ctx.state.page.class = PaperClass::Custom;
|
ctx.state.page.class = PaperClass::Custom;
|
||||||
ctx.state.page.size.width = width;
|
ctx.state.page.size.width = width;
|
||||||
ctx.state.page.expand.horizontal = Expansion::Fill;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(height) = height {
|
if let Some(height) = height {
|
||||||
ctx.state.page.class = PaperClass::Custom;
|
ctx.state.page.class = PaperClass::Custom;
|
||||||
ctx.state.page.size.height = height;
|
ctx.state.page.size.height = height;
|
||||||
ctx.state.page.expand.vertical = Expansion::Fill;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(margins) = margins {
|
if let Some(margins) = margins {
|
||||||
@ -83,7 +80,6 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
|
|||||||
if flip.unwrap_or(false) {
|
if flip.unwrap_or(false) {
|
||||||
let page = &mut ctx.state.page;
|
let page = &mut ctx.state.page;
|
||||||
std::mem::swap(&mut page.size.width, &mut page.size.height);
|
std::mem::swap(&mut page.size.width, &mut page.size.height);
|
||||||
std::mem::swap(&mut page.expand.horizontal, &mut page.expand.vertical);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.set_dirs(Gen::new(main, cross));
|
ctx.set_dirs(Gen::new(main, cross));
|
||||||
|
@ -26,17 +26,13 @@ pub fn box_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
|
|||||||
let color = args.get(ctx, "color");
|
let color = args.get(ctx, "color");
|
||||||
let body = args.find::<ValueTemplate>(ctx).unwrap_or_default();
|
let body = args.find::<ValueTemplate>(ctx).unwrap_or_default();
|
||||||
|
|
||||||
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
|
|
||||||
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
|
|
||||||
|
|
||||||
Value::template("box", move |ctx| {
|
Value::template("box", move |ctx| {
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
|
|
||||||
ctx.set_dirs(Gen::new(main, cross));
|
ctx.set_dirs(Gen::new(main, cross));
|
||||||
|
|
||||||
let child = ctx.exec_body(&body, expand);
|
let child = ctx.exec(&body);
|
||||||
let fixed = NodeFixed { width, height, child };
|
let fixed = NodeFixed { width, height, child };
|
||||||
|
|
||||||
if let Some(color) = color {
|
if let Some(color) = color {
|
||||||
ctx.push(NodeBackground {
|
ctx.push(NodeBackground {
|
||||||
fill: Fill::Color(color),
|
fill: Fill::Color(color),
|
||||||
|
BIN
tests/ref/expand.png
Normal file
BIN
tests/ref/expand.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
14
tests/typ/expand.typ
Normal file
14
tests/typ/expand.typ
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Test fit/fill expansion.
|
||||||
|
|
||||||
|
---
|
||||||
|
#let right(body) = align(right, body)
|
||||||
|
#let pad(body) = pad(left: 10pt, right: 10pt, body)
|
||||||
|
|
||||||
|
// Top-level paragraph fills page, boxed paragraph only when width is fixed.
|
||||||
|
L #right[R] \
|
||||||
|
#box(width: 50pt)[L #right[R]] \
|
||||||
|
#box[L #right[R]] \
|
||||||
|
|
||||||
|
// Pad inherits expansion behaviour.
|
||||||
|
#pad[PL #right[PR]] \
|
||||||
|
#box(pad[PL #right[PR]])
|
@ -20,8 +20,8 @@ use typst::eval::{EvalContext, Scope, Value, ValueArgs, ValueFunc};
|
|||||||
use typst::exec::State;
|
use typst::exec::State;
|
||||||
use typst::export::pdf;
|
use typst::export::pdf;
|
||||||
use typst::font::FsIndexExt;
|
use typst::font::FsIndexExt;
|
||||||
use typst::geom::{Length, Point, Sides, Size, Spec};
|
use typst::geom::{Length, Point, Sides, Size};
|
||||||
use typst::layout::{Element, Expansion, Fill, Frame, Geometry, Image, Shape};
|
use typst::layout::{Element, Fill, Frame, Geometry, Image, Shape};
|
||||||
use typst::library;
|
use typst::library;
|
||||||
use typst::parse::{LineMap, Scanner};
|
use typst::parse::{LineMap, Scanner};
|
||||||
use typst::shaping::Shaped;
|
use typst::shaping::Shaped;
|
||||||
@ -202,7 +202,6 @@ fn test_part(
|
|||||||
// large and fit them to match their content.
|
// large and fit them to match their content.
|
||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
state.page.size = Size::new(Length::pt(120.0), Length::raw(f64::INFINITY));
|
state.page.size = Size::new(Length::pt(120.0), Length::raw(f64::INFINITY));
|
||||||
state.page.expand = Spec::new(Expansion::Fill, Expansion::Fit);
|
|
||||||
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
|
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
|
||||||
|
|
||||||
let Pass { output: mut frames, diags } = typeset(env, &src, &scope, state);
|
let Pass { output: mut frames, diags } = typeset(env, &src, &scope, state);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user