Unbounded pages 🌌
@ -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<Expansion>,
|
||||
padding: Sides<Linear>,
|
||||
dirs: LayoutDirs,
|
||||
align: ChildAlign,
|
||||
|
@ -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,
|
||||
});
|
||||
|
||||
|
@ -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<Expansion>,
|
||||
/// 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<Option<Linear>>,
|
||||
@ -56,6 +59,7 @@ impl StatePage {
|
||||
Self {
|
||||
class: paper.class,
|
||||
size: paper.size(),
|
||||
expand: Spec::uniform(Expansion::Fill),
|
||||
margins: Sides::uniform(None),
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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<Size>,
|
||||
/// 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 {
|
||||
|
@ -39,10 +39,8 @@ impl From<NodePad> for NodeAny {
|
||||
fn shrink(areas: &Areas, padding: Sides<Linear>) -> 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),
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<Expansion>,
|
||||
pub expand: Spec<Expansion>,
|
||||
/// The nodes to be stacked.
|
||||
pub children: Vec<Node>,
|
||||
}
|
||||
@ -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),
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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");
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 120 B After Width: | Height: | Size: 94 B |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 821 B |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.7 KiB |
@ -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
|
||||
|
@ -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]
|
||||
]
|
||||
|
||||
---
|
||||
|
@ -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!
|
||||
|
||||
|
@ -8,15 +8,12 @@
|
||||
####### Seven
|
||||
|
||||
---
|
||||
// Is a heading.
|
||||
// Heading vs. no heading.
|
||||
|
||||
/**/ # Heading
|
||||
{[## Heading]}
|
||||
[box][### Heading]
|
||||
|
||||
---
|
||||
// Is no heading.
|
||||
|
||||
\# No heading
|
||||
|
||||
Text with # hashtag
|
||||
|
@ -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<Frame>) {
|
||||
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);
|
||||
|