Block sizing

This commit is contained in:
Laurenz 2023-02-13 15:14:25 +01:00
parent 72b60dfde7
commit b1b4e52af9
22 changed files with 220 additions and 74 deletions

View File

@ -287,17 +287,17 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html {
let source = Source::new(SourceId::from_u16(0), Path::new("main.typ"), compile); let source = Source::new(SourceId::from_u16(0), Path::new("main.typ"), compile);
let world = DocWorld(source); let world = DocWorld(source);
let mut frame = match typst::compile(&world, &world.0) { let mut frames = match typst::compile(&world, &world.0) {
Ok(doc) => doc.pages.into_iter().next().unwrap(), Ok(doc) => doc.pages,
Err(err) => panic!("failed to compile {text}: {err:?}"), Err(err) => panic!("failed to compile {text}: {err:?}"),
}; };
if let Some([x, y, w, h]) = zoom { if let Some([x, y, w, h]) = zoom {
frame.translate(Point::new(-x, -y)); frames[0].translate(Point::new(-x, -y));
*frame.size_mut() = Size::new(w, h); *frames[0].size_mut() = Size::new(w, h);
} }
resolver.example(highlighted, frame) resolver.example(highlighted, &frames)
} }
/// World for example compilations. /// World for example compilations.

View File

@ -67,7 +67,7 @@ pub trait Resolver {
fn image(&self, filename: &str, data: &[u8]) -> String; fn image(&self, filename: &str, data: &[u8]) -> String;
/// Produce HTML for an example. /// Produce HTML for an example.
fn example(&self, source: Html, frame: Frame) -> Html; fn example(&self, source: Html, frames: &[Frame]) -> Html;
} }
/// Details about a documentation page and its children. /// Details about a documentation page and its children.

View File

@ -41,20 +41,13 @@ use crate::prelude::*;
/// - height: `Rel<Length>` (named) /// - height: `Rel<Length>` (named)
/// The height of the box. /// The height of the box.
/// ///
/// - baseline: `Rel<Length>` (named)
/// An amount to shift the box's baseline by.
///
/// ```example
/// Image: #box(baseline: 40%, image("tiger.jpg", width: 2cm)).
/// ```
///
/// ## Category /// ## Category
/// layout /// layout
#[func] #[func]
#[capable(Layout)] #[capable(Layout)]
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct BoxNode { pub struct BoxNode {
/// The content to be sized. /// The box's content.
pub body: Content, pub body: Content,
/// The box's width. /// The box's width.
pub width: Sizing, pub width: Sizing,
@ -64,7 +57,11 @@ pub struct BoxNode {
#[node] #[node]
impl BoxNode { impl BoxNode {
/// The box's baseline shift. /// An amount to shift the box's baseline by.
///
/// ```example
/// Image: #box(baseline: 40%, image("tiger.jpg", width: 2cm)).
/// ```
#[property(resolve)] #[property(resolve)]
pub const BASELINE: Rel<Length> = Rel::zero(); pub const BASELINE: Rel<Length> = Rel::zero();
@ -127,11 +124,12 @@ impl Layout for BoxNode {
// Resolve the sizing to a concrete size. // Resolve the sizing to a concrete size.
let sizing = Axes::new(width, self.height); let sizing = Axes::new(width, self.height);
let expand = sizing.as_ref().map(Smart::is_custom);
let size = sizing let size = sizing
.resolve(styles) .resolve(styles)
.zip(regions.base()) .zip(regions.base())
.map(|(s, b)| s.map(|v| v.relative_to(b))) .map(|(s, b)| s.map(|v| v.relative_to(b)))
.unwrap_or(regions.size); .unwrap_or(regions.base());
// Apply inset. // Apply inset.
let mut child = self.body.clone(); let mut child = self.body.clone();
@ -142,8 +140,6 @@ impl Layout for BoxNode {
// Select the appropriate base and expansion for the child depending // Select the appropriate base and expansion for the child depending
// on whether it is automatically or relatively sized. // on whether it is automatically or relatively sized.
let is_auto = sizing.as_ref().map(Smart::is_auto);
let expand = regions.expand | !is_auto;
let pod = Regions::one(size, expand); let pod = Regions::one(size, expand);
let mut frame = child.layout(vt, styles, pod)?.into_frame(); let mut frame = child.layout(vt, styles, pod)?.into_frame();
@ -181,9 +177,9 @@ impl Layout for BoxNode {
/// ///
/// ## Examples /// ## Examples
/// With a block, you can give a background to content while still allowing it /// With a block, you can give a background to content while still allowing it
/// to break across multiple pages. The documentation examples can only have a /// to break across multiple pages.
/// single page, but the example below demonstrates how this would work.
/// ```example /// ```example
/// #set page(height: 100pt)
/// #block( /// #block(
/// fill: luma(230), /// fill: luma(230),
/// inset: 8pt, /// inset: 8pt,
@ -204,27 +200,55 @@ impl Layout for BoxNode {
/// More text. /// More text.
/// ``` /// ```
/// ///
/// Last but not least, set rules for the block function can be used to
/// configure the spacing around arbitrary block-level elements.
/// ```example
/// #set align(center)
/// #show math.formula: set block(above: 8pt, below: 16pt)
///
/// This sum of $x$ and $y$:
/// $ x + y = z $
/// A second paragraph.
/// ```
///
/// ## Parameters /// ## Parameters
/// - body: `Content` (positional) /// - body: `Content` (positional)
/// The contents of the block. /// The contents of the block.
/// ///
/// - width: `Smart<Rel<Length>>` (named)
/// The block's width.
///
/// ```example
/// #set align(center)
/// #block(
/// width: 60%,
/// inset: 8pt,
/// fill: silver,
/// lorem(10),
/// )
/// ```
///
/// - height: `Smart<Rel<Length>>` (named)
/// The block's height. When the height is larger than the remaining space on
/// a page and [`breakable`]($func/block.breakable) is `{true}`, the block
/// will continue on the next page with the remaining height.
///
/// ```example
/// #set page(height: 80pt)
/// #set align(center)
/// #block(
/// width: 80%,
/// height: 150%,
/// fill: aqua,
/// )
/// ```
///
/// - spacing: `Spacing` (named, settable) /// - spacing: `Spacing` (named, settable)
/// The spacing around this block. /// The spacing around this block. This is shorthand to set `above` and
/// `below` to the same value.
///
/// ```example
/// #set align(center)
/// #show math.formula: set block(above: 8pt, below: 16pt)
///
/// This sum of $x$ and $y$:
/// $ x + y = z $
/// A second paragraph.
/// ```
/// ///
/// - above: `Spacing` (named, settable) /// - above: `Spacing` (named, settable)
/// The spacing between this block and its predecessor. Takes precedence over /// The spacing between this block and its predecessor. Takes precedence over
/// `spacing`. /// `spacing`. Can be used in combination with a show rule to adjust the
/// spacing around arbitrary block-level elements.
/// ///
/// The default value is `{1.2em}`. /// The default value is `{1.2em}`.
/// ///
@ -240,11 +264,30 @@ impl Layout for BoxNode {
#[capable(Layout)] #[capable(Layout)]
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
pub struct BlockNode { pub struct BlockNode {
/// The block's content.
pub body: Content, pub body: Content,
/// The box's width.
pub width: Smart<Rel<Length>>,
/// The box's height.
pub height: Smart<Rel<Length>>,
} }
#[node] #[node]
impl BlockNode { impl BlockNode {
/// Whether the block can be broken and continue on the next page.
///
/// Defaults to `{true}`.
/// ```example
/// #set page(height: 80pt)
/// The following block will
/// jump to its own page.
/// #block(
/// breakable: false,
/// lorem(15),
/// )
/// ```
pub const BREAKABLE: bool = true;
/// The block's background color. See the /// The block's background color. See the
/// [rectangle's documentation]($func/rect.fill) for more details. /// [rectangle's documentation]($func/rect.fill) for more details.
pub const FILL: Option<Paint> = None; pub const FILL: Option<Paint> = None;
@ -285,7 +328,9 @@ impl BlockNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let body = args.eat()?.unwrap_or_default(); let body = args.eat()?.unwrap_or_default();
Ok(Self { body }.pack()) let width = args.named("width")?.unwrap_or_default();
let height = args.named("height")?.unwrap_or_default();
Ok(Self { body, width, height }.pack())
} }
fn set(...) { fn set(...) {
@ -315,8 +360,52 @@ impl Layout for BlockNode {
child = child.clone().padded(inset.map(|side| side.map(Length::from))); child = child.clone().padded(inset.map(|side| side.map(Length::from)));
} }
// Resolve the sizing to a concrete size.
let sizing = Axes::new(self.width, self.height);
let mut expand = sizing.as_ref().map(Smart::is_custom);
let mut size = sizing
.resolve(styles)
.zip(regions.base())
.map(|(s, b)| s.map(|v| v.relative_to(b)))
.unwrap_or(regions.base());
// Layout the child. // Layout the child.
let mut frames = child.layout(vt, styles, regions)?.into_frames(); let mut frames = if styles.get(Self::BREAKABLE) {
// Measure to ensure frames for all regions have the same width.
if self.width == Smart::Auto {
let pod = Regions::one(size, Axes::splat(false));
let frame = child.layout(vt, styles, pod)?.into_frame();
size.x = frame.width();
expand.x = true;
}
let mut pod = regions;
pod.size.x = size.x;
pod.expand = expand;
// Generate backlog for fixed height.
let mut heights = vec![];
if self.height.is_custom() {
let mut remaining = size.y;
for region in regions.iter() {
let limited = region.y.min(remaining);
heights.push(limited);
remaining -= limited;
if Abs::zero().fits(remaining) {
break;
}
}
pod.size.y = heights[0];
pod.backlog = &heights[1..];
pod.last = None;
}
child.layout(vt, styles, pod)?.into_frames()
} else {
let pod = Regions::one(size, expand);
child.layout(vt, styles, pod)?.into_frames()
};
// Prepare fill and stroke. // Prepare fill and stroke.
let fill = styles.get(Self::FILL); let fill = styles.get(Self::FILL);
@ -326,9 +415,14 @@ impl Layout for BlockNode {
// Add fill and/or stroke. // Add fill and/or stroke.
if fill.is_some() || stroke.iter().any(Option::is_some) { if fill.is_some() || stroke.iter().any(Option::is_some) {
let mut skip = false;
if let [first, rest @ ..] = frames.as_slice() {
skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
}
let outset = styles.get(Self::OUTSET); let outset = styles.get(Self::OUTSET);
let radius = styles.get(Self::RADIUS); let radius = styles.get(Self::RADIUS);
for frame in &mut frames { for frame in frames.iter_mut().skip(skip as usize) {
frame.fill_and_stroke(fill, stroke, outset, radius); frame.fill_and_stroke(fill, stroke, outset, radius);
} }
} }

View File

@ -25,12 +25,21 @@ use crate::prelude::*;
/// #enum[First][Second] /// #enum[First][Second]
/// ``` /// ```
/// ///
/// You can easily switch all your enumerations to a different numbering style
/// with a set rule.
/// ```example
/// #set enum(numbering: "a)")
///
/// + Starting off ...
/// + Don't forget step two
/// ```
///
/// ## Syntax /// ## Syntax
/// This functions also has dedicated syntax: /// This functions also has dedicated syntax:
/// ///
/// - Starting a line with a plus sign creates an automatically numbered /// - Starting a line with a plus sign creates an automatically numbered
/// enumeration item. /// enumeration item.
/// - Start a line with a number followed by a dot creates an explicitly /// - Starting a line with a number followed by a dot creates an explicitly
/// numbered enumeration item. /// numbered enumeration item.
/// ///
/// Enumeration items can contain multiple paragraphs and other block-level /// Enumeration items can contain multiple paragraphs and other block-level
@ -98,10 +107,13 @@ impl EnumNode {
/// ///
/// ```example /// ```example
/// #set enum(numbering: "(a)") /// #set enum(numbering: "(a)")
///
/// + Different /// + Different
/// + Numbering /// + Numbering
/// + Style /// + Style
///
/// #set enum(numbering: n => super[#n])
/// + Superscript
/// + Numbering!
/// ``` /// ```
#[property(referenced)] #[property(referenced)]
pub const NUMBERING: Numbering = pub const NUMBERING: Numbering =

View File

@ -44,7 +44,10 @@ impl Layout for FlowNode {
} else if child.has::<dyn Layout>() { } else if child.has::<dyn Layout>() {
layouter.layout_multiple(vt, child, styles)?; layouter.layout_multiple(vt, child, styles)?;
} else if child.is::<ColbreakNode>() { } else if child.is::<ColbreakNode>() {
layouter.finish_region(); if !layouter.regions.backlog.is_empty() || layouter.regions.last.is_some()
{
layouter.finish_region();
}
} else { } else {
panic!("unexpected flow child: {child:?}"); panic!("unexpected flow child: {child:?}");
} }
@ -207,7 +210,10 @@ impl<'a> FlowLayouter<'a> {
// Layout the block itself. // Layout the block itself.
let sticky = styles.get(BlockNode::STICKY); let sticky = styles.get(BlockNode::STICKY);
let fragment = block.layout(vt, styles, self.regions)?; let fragment = block.layout(vt, styles, self.regions)?;
for frame in fragment { for (i, frame) in fragment.into_iter().enumerate() {
if i > 0 {
self.finish_region();
}
self.layout_item(FlowItem::Frame(frame, aligns, sticky)); self.layout_item(FlowItem::Frame(frame, aligns, sticky));
} }
@ -264,8 +270,7 @@ impl<'a> FlowLayouter<'a> {
// Determine the size of the flow in this region depending on whether // Determine the size of the flow in this region depending on whether
// the region expands. // the region expands.
let mut size = self.expand.select(self.full, used); let mut size = self.expand.select(self.full, used).min(self.full);
size.y.set_min(self.full.y);
// Account for fractional spacing in the size calculation. // Account for fractional spacing in the size calculation.
let remaining = self.full.y - used.y; let remaining = self.full.y - used.y;

View File

@ -37,7 +37,7 @@ use super::Sizing;
/// ///
/// ## Example /// ## Example
/// ```example /// ```example
/// #set text(10pt, weight: "bold") /// #set text(10pt, style: "italic")
/// #let cell = rect.with( /// #let cell = rect.with(
/// inset: 8pt, /// inset: 8pt,
/// fill: rgb("e4e5ea"), /// fill: rgb("e4e5ea"),

View File

@ -13,7 +13,7 @@ use crate::prelude::*;
/// ///
/// #pad(x: 16pt, image("typing.jpg")) /// #pad(x: 16pt, image("typing.jpg"))
/// _Typing speeds can be /// _Typing speeds can be
/// measured in words per minute._ /// measured in words per minute._
/// ``` /// ```
/// ///
/// ## Parameters /// ## Parameters

View File

@ -351,14 +351,14 @@ impl Debug for PageNode {
/// more details on compound theory. /// more details on compound theory.
/// #pagebreak() /// #pagebreak()
/// ///
/// // Examples only render the first
/// // page, so this is not visible.
/// == Compound Theory /// == Compound Theory
/// In 1984, the first ...
/// ``` /// ```
/// ///
/// ## Parameters /// ## Parameters
/// - weak: `bool` (named) /// - weak: `bool` (named)
/// If `{true}`, the page break is skipped if the current page is already empty. /// If `{true}`, the page break is skipped if the current page is already
/// empty.
/// ///
/// ## Category /// ## Category
/// layout /// layout

View File

@ -16,8 +16,8 @@ use crate::prelude::*;
/// #place( /// #place(
/// top + right, /// top + right,
/// square( /// square(
/// width: 10pt, /// width: 20pt,
/// stroke: 1pt + blue /// stroke: 2pt + blue
/// ), /// ),
/// ) /// )
/// ``` /// ```

View File

@ -147,7 +147,12 @@ impl Show for HeadingNode {
if numbers != Value::None { if numbers != Value::None {
realized = numbers.display() + SpaceNode.pack() + realized; realized = numbers.display() + SpaceNode.pack() + realized;
} }
Ok(BlockNode { body: realized }.pack()) Ok(BlockNode {
body: realized,
width: Smart::Auto,
height: Smart::Auto,
}
.pack())
} }
} }

View File

@ -199,7 +199,12 @@ impl Show for RawNode {
}; };
if self.block { if self.block {
realized = BlockNode { body: realized }.pack(); realized = BlockNode {
body: realized,
width: Smart::Auto,
height: Smart::Auto,
}
.pack();
} }
Ok(realized) Ok(realized)

View File

@ -160,6 +160,8 @@ impl Layout for RectNode {
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
layout( layout(
vt, vt,
styles,
regions,
ShapeKind::Rect, ShapeKind::Rect,
&self.body, &self.body,
Axes::new(self.width, self.height), Axes::new(self.width, self.height),
@ -168,8 +170,6 @@ impl Layout for RectNode {
styles.get(Self::INSET), styles.get(Self::INSET),
styles.get(Self::OUTSET), styles.get(Self::OUTSET),
styles.get(Self::RADIUS), styles.get(Self::RADIUS),
styles,
regions,
) )
} }
} }
@ -278,6 +278,8 @@ impl Layout for SquareNode {
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
layout( layout(
vt, vt,
styles,
regions,
ShapeKind::Square, ShapeKind::Square,
&self.body, &self.body,
Axes::new(self.width, self.height), Axes::new(self.width, self.height),
@ -286,8 +288,6 @@ impl Layout for SquareNode {
styles.get(Self::INSET), styles.get(Self::INSET),
styles.get(Self::OUTSET), styles.get(Self::OUTSET),
styles.get(Self::RADIUS), styles.get(Self::RADIUS),
styles,
regions,
) )
} }
} }
@ -372,6 +372,8 @@ impl Layout for EllipseNode {
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
layout( layout(
vt, vt,
styles,
regions,
ShapeKind::Ellipse, ShapeKind::Ellipse,
&self.body, &self.body,
Axes::new(self.width, self.height), Axes::new(self.width, self.height),
@ -380,8 +382,6 @@ impl Layout for EllipseNode {
styles.get(Self::INSET), styles.get(Self::INSET),
styles.get(Self::OUTSET), styles.get(Self::OUTSET),
Corners::splat(Rel::zero()), Corners::splat(Rel::zero()),
styles,
regions,
) )
} }
} }
@ -485,6 +485,8 @@ impl Layout for CircleNode {
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
layout( layout(
vt, vt,
styles,
regions,
ShapeKind::Circle, ShapeKind::Circle,
&self.body, &self.body,
Axes::new(self.width, self.height), Axes::new(self.width, self.height),
@ -493,8 +495,6 @@ impl Layout for CircleNode {
styles.get(Self::INSET), styles.get(Self::INSET),
styles.get(Self::OUTSET), styles.get(Self::OUTSET),
Corners::splat(Rel::zero()), Corners::splat(Rel::zero()),
styles,
regions,
) )
} }
} }
@ -502,6 +502,8 @@ impl Layout for CircleNode {
/// Layout a shape. /// Layout a shape.
fn layout( fn layout(
vt: &mut Vt, vt: &mut Vt,
styles: StyleChain,
regions: Regions,
kind: ShapeKind, kind: ShapeKind,
body: &Option<Content>, body: &Option<Content>,
sizing: Axes<Smart<Rel<Length>>>, sizing: Axes<Smart<Rel<Length>>>,
@ -510,8 +512,6 @@ fn layout(
mut inset: Sides<Rel<Abs>>, mut inset: Sides<Rel<Abs>>,
outset: Sides<Rel<Abs>>, outset: Sides<Rel<Abs>>,
radius: Corners<Rel<Abs>>, radius: Corners<Rel<Abs>>,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
let resolved = sizing let resolved = sizing
.zip(regions.base()) .zip(regions.base())

View File

@ -17,7 +17,7 @@ use crate::model::{
use crate::util::EcoString; use crate::util::EcoString;
/// A finished document with metadata and page frames. /// A finished document with metadata and page frames.
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone, Hash)]
pub struct Document { pub struct Document {
/// The page frames. /// The page frames.
pub pages: Vec<Frame>, pub pages: Vec<Frame>,
@ -28,7 +28,7 @@ pub struct Document {
} }
/// A finished layout with elements at fixed positions. /// A finished layout with elements at fixed positions.
#[derive(Default, Clone)] #[derive(Default, Clone, Hash)]
pub struct Frame { pub struct Frame {
/// The size of the frame. /// The size of the frame.
size: Size, size: Size,
@ -304,12 +304,16 @@ impl Frame {
/// Arbitrarily transform the contents of the frame. /// Arbitrarily transform the contents of the frame.
pub fn transform(&mut self, transform: Transform) { pub fn transform(&mut self, transform: Transform) {
self.group(|g| g.transform = transform); if !self.is_empty() {
self.group(|g| g.transform = transform);
}
} }
/// Clip the contents of a frame to its size. /// Clip the contents of a frame to its size.
pub fn clip(&mut self) { pub fn clip(&mut self) {
self.group(|g| g.clips = true); if !self.is_empty() {
self.group(|g| g.clips = true);
}
} }
/// Wrap the frame's contents in a group and modify that group with `f`. /// Wrap the frame's contents in a group and modify that group with `f`.
@ -386,7 +390,7 @@ impl Debug for Frame {
} }
/// The building block frames are composed of. /// The building block frames are composed of.
#[derive(Clone)] #[derive(Clone, Hash)]
pub enum Element { pub enum Element {
/// A group of elements. /// A group of elements.
Group(Group), Group(Group),
@ -413,7 +417,7 @@ impl Debug for Element {
} }
/// A group of elements with optional clipping. /// A group of elements with optional clipping.
#[derive(Clone)] #[derive(Clone, Hash)]
pub struct Group { pub struct Group {
/// The group's frame. /// The group's frame.
pub frame: Frame, pub frame: Frame,
@ -442,7 +446,7 @@ impl Debug for Group {
} }
/// A run of shaped text. /// A run of shaped text.
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq, Hash)]
pub struct Text { pub struct Text {
/// The font the glyphs are contained in. /// The font the glyphs are contained in.
pub font: Font, pub font: Font,
@ -477,7 +481,7 @@ impl Debug for Text {
} }
/// A glyph in a run of shaped text. /// A glyph in a run of shaped text.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Glyph { pub struct Glyph {
/// The glyph's index in the font. /// The glyph's index in the font.
pub id: u16, pub id: u16,

View File

@ -73,7 +73,7 @@ pub trait Get<Index> {
} }
/// A geometric shape with optional fill and stroke. /// A geometric shape with optional fill and stroke.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Shape { pub struct Shape {
/// The shape's geometry. /// The shape's geometry.
pub geometry: Geometry, pub geometry: Geometry,
@ -84,7 +84,7 @@ pub struct Shape {
} }
/// A shape's geometry. /// A shape's geometry.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Geometry { pub enum Geometry {
/// A line to a point (relative to its position). /// A line to a point (relative to its position).
Line(Point), Line(Point),

View File

@ -1,11 +1,11 @@
use super::*; use super::*;
/// A bezier path. /// A bezier path.
#[derive(Debug, Default, Clone, Eq, PartialEq)] #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct Path(pub Vec<PathElement>); pub struct Path(pub Vec<PathElement>);
/// An element in a bezier path. /// An element in a bezier path.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum PathElement { pub enum PathElement {
MoveTo(Point), MoveTo(Point),
LineTo(Point), LineTo(Point),

BIN
tests/ref/bugs/flow-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,5 @@
// In this bug, a frame intended for the second region ended up in the first.
---
#set page(height: 105pt)
#block(lorem(20))

View File

@ -0,0 +1,16 @@
// Test blocks with fixed height.
---
#set page(height: 100pt)
#set align(center)
#lorem(10)
#block(width: 80%, height: 60pt, fill: aqua)
#lorem(6)
#block(
breakable: false,
width: 100%,
inset: 4pt,
fill: aqua,
lorem(8) + colbreak(),
)

View File

@ -1,6 +1,6 @@
#set page(height: 100pt) #set page(height: 100pt)
#let words = lorem(18).split() #let words = lorem(18).split()
#block(inset: 8pt, fill: aqua, stroke: aqua.darken(30%))[ #block(inset: 8pt, width: 100%, fill: aqua, stroke: aqua.darken(30%))[
#words.slice(0, 12).join(" ") #words.slice(0, 12).join(" ")
#box(fill: teal, outset: 2pt)[incididunt] #box(fill: teal, outset: 2pt)[incididunt]
#words.slice(12).join(" ") #words.slice(12).join(" ")