diff --git a/src/eval/context.rs b/src/eval/context.rs index 64a8fbbed..1e09aaaf7 100644 --- a/src/eval/context.rs +++ b/src/eval/context.rs @@ -95,6 +95,7 @@ impl EvalContext { pub fn start_page_group(&mut self, softness: Softness) { self.start_group(PageGroup { size: self.state.page.size, + expand: self.state.page.expand, padding: self.state.page.margins(), dirs: self.state.dirs, align: self.state.align, @@ -124,7 +125,7 @@ impl EvalContext { child: NodeStack { dirs: group.dirs, align: group.align, - expansion: Gen::uniform(Expansion::Fill), + expand: group.expand, children, } .into(), @@ -281,6 +282,7 @@ pub enum Softness { #[derive(Debug)] struct PageGroup { size: Size, + expand: Spec, padding: Sides, dirs: LayoutDirs, align: ChildAlign, diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 0e6fa79fd..9abc1074f 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -19,7 +19,7 @@ use std::rc::Rc; use crate::color::Color; use crate::diag::Pass; use crate::env::SharedEnv; -use crate::geom::{Angle, Gen, Length, Relative}; +use crate::geom::{Angle, Length, Relative, Spec}; use crate::layout::{self, Expansion, NodeSpacing, NodeStack}; use crate::syntax::*; @@ -137,7 +137,7 @@ impl Eval for Spanned<&NodeRaw> { ctx.push(NodeStack { dirs: ctx.state.dirs, align: ctx.state.align, - expansion: Gen::uniform(Expansion::Fit), + expand: Spec::uniform(Expansion::Fit), children, }); diff --git a/src/eval/state.rs b/src/eval/state.rs index 2a8ee2f00..ce6bd0099 100644 --- a/src/eval/state.rs +++ b/src/eval/state.rs @@ -4,8 +4,9 @@ use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, Font use super::Scope; use crate::geom::{ - Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size, + Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size, Spec, }; +use crate::layout::Expansion; use crate::paper::{Paper, PaperClass, PAPER_A4}; /// The evaluation state. @@ -45,6 +46,8 @@ pub struct StatePage { pub class: PaperClass, /// The width and height of the page. pub size: Size, + /// Whether the expand the pages to the `size` or to fit the content. + pub expand: Spec, /// The amount of white space in the order [left, top, right, bottom]. If a /// side is set to `None`, the default for the paper class is used. pub margins: Sides>, @@ -56,6 +59,7 @@ impl StatePage { Self { class: paper.class, size: paper.size(), + expand: Spec::uniform(Expansion::Fill), margins: Sides::uniform(None), } } diff --git a/src/geom/length.rs b/src/geom/length.rs index f4d8682ed..bfb1d6681 100644 --- a/src/geom/length.rs +++ b/src/geom/length.rs @@ -81,6 +81,16 @@ impl Length { Self { raw: self.raw.max(other.raw) } } + /// Whether the length is finite. + pub fn is_finite(self) -> bool { + self.raw.is_finite() + } + + /// Whether the length is infinite. + pub fn is_infinite(self) -> bool { + self.raw.is_infinite() + } + /// Whether the length is `NaN`. pub fn is_nan(self) -> bool { self.raw.is_nan() diff --git a/src/geom/relative.rs b/src/geom/relative.rs index 8fd430af8..d39ead3ae 100644 --- a/src/geom/relative.rs +++ b/src/geom/relative.rs @@ -26,7 +26,12 @@ impl Relative { /// Resolve this relative to the given `length`. pub fn resolve(self, length: Length) -> Length { - self.get() * length + // Zero wins over infinity. + if self.0 == 0.0 { + Length::ZERO + } else { + self.get() * length + } } } diff --git a/src/geom/size.rs b/src/geom/size.rs index 1dfc8b97e..caba3d8b7 100644 --- a/src/geom/size.rs +++ b/src/geom/size.rs @@ -31,6 +31,16 @@ impl Size { self.width >= other.width && self.height >= other.height } + /// Whether both components are finite. + pub fn is_finite(self) -> bool { + self.width.is_finite() && self.height.is_finite() + } + + /// Whether any of the two components is infinite. + pub fn is_infinite(self) -> bool { + self.width.is_infinite() || self.height.is_infinite() + } + /// Whether any of the two components is `NaN`. pub fn is_nan(self) -> bool { self.width.is_nan() || self.height.is_nan() diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs index cb69b1788..60dbe4d6f 100644 --- a/src/layout/fixed.rs +++ b/src/layout/fixed.rs @@ -14,10 +14,10 @@ pub struct NodeFixed { impl Layout for NodeFixed { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { - let Area { rem, full } = areas.current; + let Areas { current, full, .. } = areas; let size = Size::new( - self.width.map(|w| w.resolve(full.width)).unwrap_or(rem.width), - self.height.map(|h| h.resolve(full.height)).unwrap_or(rem.height), + self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width), + self.height.map(|h| h.resolve(full.height)).unwrap_or(current.height), ); let areas = Areas::once(size); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 30026b9f7..714bac4b5 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -71,27 +71,13 @@ pub struct LayoutContext { pub env: SharedEnv, } -/// An 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 } - } -} - /// A collection of areas to layout into. #[derive(Debug, Clone, PartialEq)] pub struct Areas { - /// The current area. - pub current: Area, + /// The remaining size of the current area. + pub current: Size, + /// The full size the current area once had (used for relative sizing). + pub full: Size, /// A stack of followup areas (the next area is the last element). pub backlog: Vec, /// The final area that is repeated when the backlog is empty. @@ -102,7 +88,8 @@ 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), + current: size, + full: size, backlog: vec![], last: None, } @@ -111,7 +98,8 @@ impl Areas { /// Create a new sequence of areas that repeats `area` indefinitely. pub fn repeat(size: Size) -> Self { Self { - current: Area::new(size), + current: size, + full: size, backlog: vec![], last: Some(size), } @@ -120,7 +108,8 @@ impl Areas { /// 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); + self.current = size; + self.full = size; } } @@ -130,11 +119,32 @@ impl Areas { pub fn in_full_last(&self) -> bool { self.backlog.is_empty() && self.last.map_or(true, |size| { - self.current.rem.is_nan() || size.is_nan() || self.current.rem == size + self.current.is_nan() || size.is_nan() || self.current == size }) } } +/// Whether to expand or shrink a node along an axis. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Expansion { + /// Fit the content. + Fit, + /// Fill the available space. + Fill, +} + +impl Expansion { + /// Resolve the expansion to either the `fit` or `fill` length. + /// + /// Prefers `fit` if `fill` is infinite. + pub fn resolve(self, fit: Length, fill: Length) -> Length { + match self { + Self::Fill if fill.is_finite() => fill, + _ => fit, + } + } +} + /// The result of layouting a node. #[derive(Debug, Clone, PartialEq)] pub enum Layouted { @@ -158,15 +168,6 @@ impl Layouted { } } -/// Whether to expand or shrink a node along an axis. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Expansion { - /// Fit the content. - Fit, - /// Fill the available space. - Fill, -} - /// A finished layout with elements at fixed positions. #[derive(Debug, Clone, PartialEq)] pub struct Frame { diff --git a/src/layout/pad.rs b/src/layout/pad.rs index 9e2718c3e..f8a623e39 100644 --- a/src/layout/pad.rs +++ b/src/layout/pad.rs @@ -39,10 +39,8 @@ impl From for NodeAny { fn shrink(areas: &Areas, padding: Sides) -> Areas { let shrink = |size| size - padding.resolve(size).size(); Areas { - current: Area { - rem: shrink(areas.current.rem), - full: shrink(areas.current.full), - }, + current: shrink(areas.current), + full: shrink(areas.full), backlog: areas.backlog.iter().copied().map(shrink).collect(), last: areas.last.map(shrink), } diff --git a/src/layout/par.rs b/src/layout/par.rs index 7b2e4e925..2a1ad941a 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -74,7 +74,7 @@ impl<'a> ParLayouter<'a> { } fn push_spacing(&mut self, amount: Length) { - let cross_max = self.areas.current.rem.get(self.cross); + let cross_max = self.areas.current.get(self.cross); self.run_size.cross = (self.run_size.cross + amount).min(cross_max); } @@ -84,7 +84,7 @@ impl<'a> ParLayouter<'a> { } let fits = { - let mut usable = self.areas.current.rem; + let mut usable = self.areas.current; *usable.get_mut(self.cross) -= self.run_size.cross; usable.fits(frame.size) }; @@ -92,7 +92,7 @@ impl<'a> ParLayouter<'a> { if !fits { self.finish_run(); - while !self.areas.current.rem.fits(frame.size) { + while !self.areas.current.fits(frame.size) { if self.areas.in_full_last() { // TODO: Diagnose once the necessary spans exist. let _ = warning!("cannot fit frame into any area"); @@ -112,10 +112,15 @@ impl<'a> ParLayouter<'a> { } fn finish_run(&mut self) { - let full_size = Gen::new(self.run_size.main, match self.par.cross_expansion { - Expansion::Fill => self.areas.current.full.get(self.cross), - Expansion::Fit => self.run_size.cross, - }); + let full_size = { + let full = self.areas.full.switch(self.dirs); + Gen::new( + self.run_size.main, + self.par + .cross_expansion + .resolve(self.run_size.cross.min(full.cross), full.cross), + ) + }; let mut output = Frame::new(full_size.switch(self.dirs).to_size()); @@ -139,7 +144,7 @@ impl<'a> ParLayouter<'a> { self.lines.push((self.lines_size.main, output, self.run_ruler)); let main_offset = full_size.main + self.par.line_spacing; - *self.areas.current.rem.get_mut(self.main) -= main_offset; + *self.areas.current.get_mut(self.main) -= main_offset; self.lines_size.main += main_offset; self.lines_size.cross = self.lines_size.cross.max(full_size.cross); diff --git a/src/layout/stack.rs b/src/layout/stack.rs index eac631d9d..bfb93a941 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -11,7 +11,7 @@ pub struct NodeStack { /// How to align this stack in _its_ parent. pub align: ChildAlign, /// Whether to expand the axes to fill the area or to fit the content. - pub expansion: Gen, + pub expand: Spec, /// The nodes to be stacked. pub children: Vec, } @@ -66,7 +66,7 @@ impl<'a> StackLayouter<'a> { } fn push_spacing(&mut self, amount: Length) { - let main_rest = self.areas.current.rem.get_mut(self.main); + let main_rest = self.areas.current.get_mut(self.main); let capped = amount.min(*main_rest); *main_rest -= capped; self.used.main += capped; @@ -77,7 +77,7 @@ impl<'a> StackLayouter<'a> { self.finish_area(); } - while !self.areas.current.rem.fits(frame.size) { + while !self.areas.current.fits(frame.size) { if self.areas.in_full_last() { // TODO: Diagnose once the necessary spans exist. let _ = warning!("cannot fit frame into any area"); @@ -90,7 +90,7 @@ impl<'a> StackLayouter<'a> { let size = frame.size.switch(self.dirs); self.frames.push((self.used.main, frame, align)); - *self.areas.current.rem.get_mut(self.main) -= size.main; + *self.areas.current.get_mut(self.main) -= size.main; self.used.main += size.main; self.used.cross = self.used.cross.max(size.cross); self.ruler = align.main; @@ -98,16 +98,11 @@ impl<'a> StackLayouter<'a> { fn finish_area(&mut self) { let full_size = { - let full = self.areas.current.full.switch(self.dirs); + let expand = self.stack.expand.switch(self.dirs); + let full = self.areas.full.switch(self.dirs); Gen::new( - match self.stack.expansion.main { - Expansion::Fill => full.main, - Expansion::Fit => self.used.main.min(full.main), - }, - match self.stack.expansion.cross { - Expansion::Fill => full.cross, - Expansion::Fit => self.used.cross.min(full.cross), - }, + expand.main.resolve(self.used.main.min(full.main), full.main), + expand.cross.resolve(self.used.cross.min(full.cross), full.cross), ) }; diff --git a/src/library/insert.rs b/src/library/insert.rs index 8d60927a3..51cbbf523 100644 --- a/src/library/insert.rs +++ b/src/library/insert.rs @@ -55,8 +55,11 @@ struct NodeImage { impl Layout for NodeImage { fn layout(&self, _: &mut LayoutContext, areas: &Areas) -> Layouted { - let Area { rem, full } = areas.current; - let pixel_ratio = (self.dimensions.0 as f64) / (self.dimensions.1 as f64); + let Areas { current, full, .. } = areas; + + let pixel_width = self.dimensions.0 as f64; + let pixel_height = self.dimensions.1 as f64; + let pixel_ratio = pixel_width / pixel_height; let width = self.width.map(|w| w.resolve(full.width)); let height = self.height.map(|w| w.resolve(full.height)); @@ -66,12 +69,15 @@ impl Layout for NodeImage { (Some(width), None) => Size::new(width, width / pixel_ratio), (None, Some(height)) => Size::new(height * pixel_ratio, height), (None, None) => { - let ratio = rem.width / rem.height; - if ratio < pixel_ratio { - Size::new(rem.width, rem.width / pixel_ratio) - } else { + let ratio = current.width / current.height; + if ratio < pixel_ratio && current.width.is_finite() { + Size::new(current.width, current.width / pixel_ratio) + } else if current.height.is_finite() { // TODO: Fix issue with line spacing. - Size::new(rem.height * pixel_ratio, rem.height) + Size::new(current.height * pixel_ratio, current.height) + } else { + // Totally unbounded area, we have to make up something. + Size::new(Length::pt(pixel_width), Length::pt(pixel_height)) } } }; diff --git a/src/library/layout.rs b/src/library/layout.rs index 59c1954b6..0e04c5071 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -197,13 +197,12 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { let children = ctx.end_content_group(); let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit }; - let expansion = - Spec::new(fill_if(width.is_some()), fill_if(height.is_some())).switch(dirs); + let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some())); ctx.push(NodeFixed { width, height, - child: NodeStack { dirs, align, expansion, children }.into(), + child: NodeStack { dirs, align, expand, children }.into(), }); ctx.state = snapshot; @@ -271,6 +270,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value { if let Some(paper) = Paper::from_name(&name.v) { ctx.state.page.class = paper.class; ctx.state.page.size = paper.size(); + ctx.state.page.expand = Spec::uniform(Expansion::Fill); } else { ctx.diag(error!(name.span, "invalid paper name")); } @@ -279,11 +279,13 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value { if let Some(width) = args.get(ctx, "width") { ctx.state.page.class = PaperClass::Custom; ctx.state.page.size.width = width; + ctx.state.page.expand.horizontal = Expansion::Fill; } if let Some(height) = args.get(ctx, "height") { ctx.state.page.class = PaperClass::Custom; ctx.state.page.size.height = height; + ctx.state.page.expand.vertical = Expansion::Fill; } if let Some(margins) = args.get(ctx, "margins") { @@ -307,8 +309,9 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value { } if args.get(ctx, "flip").unwrap_or(false) { - let size = &mut ctx.state.page.size; - std::mem::swap(&mut size.width, &mut size.height); + let page = &mut ctx.state.page; + std::mem::swap(&mut page.size.width, &mut page.size.height); + std::mem::swap(&mut page.expand.horizontal, &mut page.expand.vertical); } let main = args.get(ctx, "main-dir"); diff --git a/tests/ref/arrays.png b/tests/ref/arrays.png index 8fda7cf6b..2d70a3f67 100644 Binary files a/tests/ref/arrays.png and b/tests/ref/arrays.png differ diff --git a/tests/ref/basics.png b/tests/ref/basics.png index d65b7fe54..fbe85320d 100644 Binary files a/tests/ref/basics.png and b/tests/ref/basics.png differ diff --git a/tests/ref/comments.png b/tests/ref/comments.png index 399d25a17..e3e42d231 100644 Binary files a/tests/ref/comments.png and b/tests/ref/comments.png differ diff --git a/tests/ref/dictionaries.png b/tests/ref/dictionaries.png index 42cb431d6..aa40549e6 100644 Binary files a/tests/ref/dictionaries.png and b/tests/ref/dictionaries.png differ diff --git a/tests/ref/empty.png b/tests/ref/empty.png index 812a3758c..87c06b0a8 100644 Binary files a/tests/ref/empty.png and b/tests/ref/empty.png differ diff --git a/tests/ref/escaping.png b/tests/ref/escaping.png index 06c0310dc..575a4b2fc 100644 Binary files a/tests/ref/escaping.png and b/tests/ref/escaping.png differ diff --git a/tests/ref/func-font.png b/tests/ref/func-font.png index 188414903..1e025c6f2 100644 Binary files a/tests/ref/func-font.png and b/tests/ref/func-font.png differ diff --git a/tests/ref/func-hv.png b/tests/ref/func-hv.png index 6f47ae09b..ee649fb67 100644 Binary files a/tests/ref/func-hv.png and b/tests/ref/func-hv.png differ diff --git a/tests/ref/func-image.png b/tests/ref/func-image.png index e2b647bfe..70f263e6e 100644 Binary files a/tests/ref/func-image.png and b/tests/ref/func-image.png differ diff --git a/tests/ref/func-page.png b/tests/ref/func-page.png index 9bde2dae6..fb1b7b398 100644 Binary files a/tests/ref/func-page.png and b/tests/ref/func-page.png differ diff --git a/tests/ref/func-pagebreak.png b/tests/ref/func-pagebreak.png index 50686ff97..16eae0e49 100644 Binary files a/tests/ref/func-pagebreak.png and b/tests/ref/func-pagebreak.png differ diff --git a/tests/ref/func-rgb.png b/tests/ref/func-rgb.png index 239a9e5a5..3060e42c3 100644 Binary files a/tests/ref/func-rgb.png and b/tests/ref/func-rgb.png differ diff --git a/tests/ref/headings.png b/tests/ref/headings.png index 3deecf8f3..1f374cce6 100644 Binary files a/tests/ref/headings.png and b/tests/ref/headings.png differ diff --git a/tests/typ/func-hv.typ b/tests/typ/func-hv.typ index f930bc129..b32a645c7 100644 --- a/tests/typ/func-hv.typ +++ b/tests/typ/func-hv.typ @@ -12,13 +12,13 @@ Add [h 10pt] [h 10pt] up // Relative to font size. Relative [h 100%] spacing +// Missing spacing. +// Error: 1:11-1:11 missing argument: spacing +Totally [h] ignored + // Swapped axes. -[page main-dir: rtl, cross-dir: ttb][ +[page main-dir: rtl, cross-dir: ttb, height: 80pt][ 1 [h 1cm] 2 3 [v 1cm] 4 [v -1cm] 5 ] - -// Missing spacing. -// Error: 1:11-1:11 missing argument: spacing -Totally [h] ignored diff --git a/tests/typ/func-image.typ b/tests/typ/func-image.typ index b0ca03577..1f94a66ff 100644 --- a/tests/typ/func-image.typ +++ b/tests/typ/func-image.typ @@ -14,7 +14,7 @@ [image "res/rhino.png"] // Fit to height of page. -[page width: 270pt][ +[page height: 40pt][ [image "res/rhino.png"] ] @@ -29,7 +29,7 @@ // Make sure the bounding-box of the image is correct. [align bottom, right][ - [image "res/tiger.jpg"] + [image "res/tiger.jpg", width: 60pt] ] --- diff --git a/tests/typ/func-page.typ b/tests/typ/func-page.typ index 465a3469e..49671e140 100644 --- a/tests/typ/func-page.typ +++ b/tests/typ/func-page.typ @@ -1,28 +1,36 @@ // Test configuring page sizes and margins. -// Set width. -[page width: 50pt][High] - -// Set height. -[page height: 50pt][Wide] +// Set width and height. +[page width: 120pt, height: 120pt] +[page width: 40pt][High] +[page height: 40pt][Wide] // Set all margins at once. -[page margins: 40pt][ +[page margins: 30pt][ [align top, left][TL] [align bottom, right][BR] ] // Set individual margins. +[page height: 40pt] [page left: 0pt | align left][Left] [page right: 0pt | align right][Right] [page top: 0pt | align top][Top] [page bottom: 0pt | align bottom][Bottom] // Ensure that specific margins override general margins. -[page margins: 0pt, left: 40pt][Overriden] +[page margins: 0pt, left: 20pt][Overriden] -// Flip the page. -[page "a10", flip: true][Flipped] +--- +// Test flipping. + +// Flipped predefined paper. +[page "a11", flip: true][Flipped A11] + +// Flipped custom page size. +[page width: 40pt, height: 120pt] +[page flip: true] +Wide --- // Test a combination of pages with bodies and normal content. @@ -40,7 +48,7 @@ Sixth --- // Test changing the layouting directions of pages. -[page main-dir: btt, cross-dir: rtl] +[page height: 50pt, main-dir: btt, cross-dir: rtl] Right to left! diff --git a/tests/typ/headings.typ b/tests/typ/headings.typ index de98eddb9..0f364a9bf 100644 --- a/tests/typ/headings.typ +++ b/tests/typ/headings.typ @@ -8,15 +8,12 @@ ####### Seven --- -// Is a heading. +// Heading vs. no heading. /**/ # Heading {[## Heading]} [box][### Heading] ---- -// Is no heading. - \# No heading Text with # hashtag diff --git a/tests/typeset.rs b/tests/typeset.rs index 499c5c04a..f67649b0c 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -18,8 +18,8 @@ use typst::env::{Env, ImageResource, ResourceLoader, SharedEnv}; use typst::eval::{Args, EvalContext, State, Value, ValueFunc}; use typst::export::pdf; use typst::font::FontLoader; -use typst::geom::{Length, Point, Sides, Size}; -use typst::layout::{Element, Frame, Image}; +use typst::geom::{Length, Point, Sides, Size, Spec}; +use typst::layout::{Element, Expansion, Frame, Image}; use typst::parse::{LineMap, Scanner}; use typst::shaping::Shaped; use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan}; @@ -153,12 +153,12 @@ fn test( let env = env.borrow(); if !frames.is_empty() { - let canvas = draw(&frames, &env, 2.0); - canvas.pixmap.save_png(png_path).unwrap(); - let pdf_data = pdf::export(&frames, &env); fs::write(pdf_path, pdf_data).unwrap(); + let canvas = draw(&frames, &env, 2.0); + canvas.pixmap.save_png(png_path).unwrap(); + if let Some(ref_path) = ref_path { if let Ok(ref_pixmap) = Pixmap::load_png(ref_path) { if canvas.pixmap != ref_pixmap { @@ -184,7 +184,11 @@ fn test_part(i: usize, src: &str, env: &SharedEnv) -> (bool, Vec) { let (compare_ref, ref_diags) = parse_metadata(src, &map); let mut state = State::default(); - state.page.size = Size::uniform(Length::pt(120.0)); + + // We want to have "unbounded" pages, so we allow them to be infinitely + // large and fit them to match their content. + 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())); pub fn dump(_: &mut EvalContext, args: &mut Args) -> Value { @@ -283,6 +287,10 @@ fn draw(frames: &[Frame], env: &Env, pixel_per_pt: f32) -> Canvas { let pixel_width = (pixel_per_pt * width.to_pt() as f32) as u32; let pixel_height = (pixel_per_pt * height.to_pt() as f32) as u32; + if pixel_width > 4000 || pixel_height > 4000 { + panic!("overlarge image: {} by {}", pixel_width, pixel_height); + } + let mut canvas = Canvas::new(pixel_width, pixel_height).unwrap(); canvas.scale(pixel_per_pt, pixel_per_pt); canvas.pixmap.fill(Color::BLACK);