mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Introduce virtual typesetter
This commit is contained in:
parent
33ab1fdbdd
commit
5110a41de1
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -159,8 +159,7 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "comemo"
|
name = "comemo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/typst/comemo#36fb31c76eb42d67244bd9c7a2630c29767912f2"
|
||||||
checksum = "bf2ceb1049619fdad21077121c28bffa1552627a1a5f0a0ee97a83e53fe45fd0"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"comemo-macros",
|
"comemo-macros",
|
||||||
"siphasher",
|
"siphasher",
|
||||||
@ -169,8 +168,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "comemo-macros"
|
name = "comemo-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/typst/comemo#36fb31c76eb42d67244bd9c7a2630c29767912f2"
|
||||||
checksum = "aa4cdbf89b08c431b3ae1ed7d571f293f620311e529b4c2595a39161312d964e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -15,7 +15,7 @@ bench = false
|
|||||||
typst-macros = { path = "macros" }
|
typst-macros = { path = "macros" }
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
bytemuck = "1"
|
bytemuck = "1"
|
||||||
comemo = "0.1"
|
comemo = { git = "https://github.com/typst/comemo" }
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
|
image = { version = "0.24", default-features = false, features = ["png", "jpeg", "gif"] }
|
||||||
miniz_oxide = "0.5"
|
miniz_oxide = "0.5"
|
||||||
|
@ -17,7 +17,7 @@ typst = { path = ".." }
|
|||||||
typst-library = { path = "../library" }
|
typst-library = { path = "../library" }
|
||||||
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
|
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
|
||||||
codespan-reporting = "0.11"
|
codespan-reporting = "0.11"
|
||||||
comemo = "0.1"
|
comemo = { git = "https://github.com/typst/comemo" }
|
||||||
dirs = "4"
|
dirs = "4"
|
||||||
elsa = "1.7"
|
elsa = "1.7"
|
||||||
memmap2 = "0.5"
|
memmap2 = "0.5"
|
||||||
|
@ -11,7 +11,7 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
typst = { path = ".." }
|
typst = { path = ".." }
|
||||||
comemo = "0.1"
|
comemo = { git = "https://github.com/typst/comemo" }
|
||||||
csv = "1"
|
csv = "1"
|
||||||
hypher = "0.1"
|
hypher = "0.1"
|
||||||
kurbo = "0.8"
|
kurbo = "0.8"
|
||||||
|
@ -34,7 +34,7 @@ impl HeadingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for HeadingNode {
|
impl Show for HeadingNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> Content {
|
||||||
BlockNode(self.body.clone()).pack()
|
BlockNode(self.body.clone()).pack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ impl<const L: ListKind> ListNode<L> {
|
|||||||
impl<const L: ListKind> Layout for ListNode<L> {
|
impl<const L: ListKind> Layout for ListNode<L> {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -104,7 +104,7 @@ impl<const L: ListKind> Layout for ListNode<L> {
|
|||||||
cells.push(Content::empty());
|
cells.push(Content::empty());
|
||||||
|
|
||||||
let label = if L == LIST || L == ENUM {
|
let label = if L == LIST || L == ENUM {
|
||||||
label.resolve(world, L, number)?.styled_with_map(map.clone())
|
label.resolve(vt, L, number)?.styled_with_map(map.clone())
|
||||||
} else {
|
} else {
|
||||||
Content::empty()
|
Content::empty()
|
||||||
};
|
};
|
||||||
@ -137,7 +137,7 @@ impl<const L: ListKind> Layout for ListNode<L> {
|
|||||||
gutter: Axes::with_y(vec![gutter.into()]),
|
gutter: Axes::with_y(vec![gutter.into()]),
|
||||||
cells,
|
cells,
|
||||||
}
|
}
|
||||||
.layout(world, styles, regions)
|
.layout(vt, styles, regions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ impl Label {
|
|||||||
/// Resolve the label based on the level.
|
/// Resolve the label based on the level.
|
||||||
pub fn resolve(
|
pub fn resolve(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &Vt,
|
||||||
kind: ListKind,
|
kind: ListKind,
|
||||||
number: usize,
|
number: usize,
|
||||||
) -> SourceResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
@ -246,7 +246,7 @@ impl Label {
|
|||||||
Self::Content(content) => content.clone(),
|
Self::Content(content) => content.clone(),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
let args = Args::new(*span, [Value::Int(number as i64)]);
|
let args = Args::new(*span, [Value::Int(number as i64)]);
|
||||||
func.call_detached(world, args)?.display()
|
func.call_detached(vt.world(), args)?.display()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ impl TableNode {
|
|||||||
impl Layout for TableNode {
|
impl Layout for TableNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -76,7 +76,7 @@ impl Layout for TableNode {
|
|||||||
|
|
||||||
let x = i % cols;
|
let x = i % cols;
|
||||||
let y = i / cols;
|
let y = i / cols;
|
||||||
if let Some(fill) = fill.resolve(world, x, y)? {
|
if let Some(fill) = fill.resolve(vt, x, y)? {
|
||||||
child = child.filled(fill);
|
child = child.filled(fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ impl Layout for TableNode {
|
|||||||
gutter: self.gutter.clone(),
|
gutter: self.gutter.clone(),
|
||||||
cells,
|
cells,
|
||||||
}
|
}
|
||||||
.layout(world, styles, regions)
|
.layout(vt, styles, regions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,17 +104,12 @@ pub enum Celled<T> {
|
|||||||
|
|
||||||
impl<T: Cast + Clone> Celled<T> {
|
impl<T: Cast + Clone> Celled<T> {
|
||||||
/// Resolve the value based on the cell position.
|
/// Resolve the value based on the cell position.
|
||||||
pub fn resolve(
|
pub fn resolve(&self, vt: &Vt, x: usize, y: usize) -> SourceResult<T> {
|
||||||
&self,
|
|
||||||
world: Tracked<dyn World>,
|
|
||||||
x: usize,
|
|
||||||
y: usize,
|
|
||||||
) -> SourceResult<T> {
|
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Value(value) => value.clone(),
|
Self::Value(value) => value.clone(),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]);
|
let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]);
|
||||||
func.call_detached(world, args)?.cast().at(*span)?
|
func.call_detached(vt.world(), args)?.cast().at(*span)?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use comemo::Track;
|
use comemo::Track;
|
||||||
|
|
||||||
use typst::model;
|
use typst::model;
|
||||||
use typst::syntax::Source;
|
use typst::syntax::Source;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ impl AlignNode {
|
|||||||
impl Layout for AlignNode {
|
impl Layout for AlignNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -44,7 +44,7 @@ impl Layout for AlignNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Layout the child.
|
// Layout the child.
|
||||||
let mut fragment = self.child.layout(world, styles.chain(&map), &pod)?;
|
let mut fragment = self.child.layout(vt, styles.chain(&map), &pod)?;
|
||||||
for (region, frame) in regions.iter().zip(&mut fragment) {
|
for (region, frame) in regions.iter().zip(&mut fragment) {
|
||||||
// Align in the target size. The target size depends on whether we
|
// Align in the target size. The target size depends on whether we
|
||||||
// should expand.
|
// should expand.
|
||||||
|
@ -29,14 +29,14 @@ impl ColumnsNode {
|
|||||||
impl Layout for ColumnsNode {
|
impl Layout for ColumnsNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// 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.first.x.is_finite() {
|
if !regions.first.x.is_finite() {
|
||||||
return self.child.layout(world, styles, regions);
|
return self.child.layout(vt, styles, regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the width of the gutter and each column.
|
// Determine the width of the gutter and each column.
|
||||||
@ -58,7 +58,7 @@ impl Layout for ColumnsNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Layout the children.
|
// Layout the children.
|
||||||
let mut frames = self.child.layout(world, styles, &pod)?.into_iter();
|
let mut frames = self.child.layout(vt, styles, &pod)?.into_iter();
|
||||||
let mut finished = vec![];
|
let mut finished = vec![];
|
||||||
|
|
||||||
let dir = styles.get(TextNode::DIR);
|
let dir = styles.get(TextNode::DIR);
|
||||||
|
@ -23,7 +23,7 @@ impl BoxNode {
|
|||||||
impl Layout for BoxNode {
|
impl Layout for BoxNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -47,7 +47,7 @@ impl Layout for BoxNode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Layout the child.
|
// Layout the child.
|
||||||
let mut frame = self.child.layout(world, styles, &pod)?.into_frame();
|
let mut frame = self.child.layout(vt, styles, &pod)?.into_frame();
|
||||||
|
|
||||||
// Ensure frame size matches regions size if expansion is on.
|
// Ensure frame size matches regions size if expansion is on.
|
||||||
let target = regions.expand.select(regions.first, frame.size());
|
let target = regions.expand.select(regions.first, frame.size());
|
||||||
@ -95,10 +95,10 @@ impl BlockNode {
|
|||||||
impl Layout for BlockNode {
|
impl Layout for BlockNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
self.0.layout(world, styles, regions)
|
self.0.layout(vt, styles, regions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ impl FlowNode {
|
|||||||
impl Layout for FlowNode {
|
impl Layout for FlowNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -33,9 +33,9 @@ impl Layout for FlowNode {
|
|||||||
} else if let Some(node) = child.to::<ParNode>() {
|
} else if let Some(node) = child.to::<ParNode>() {
|
||||||
let barrier = Style::Barrier(child.id());
|
let barrier = Style::Barrier(child.id());
|
||||||
let styles = styles.chain_one(&barrier);
|
let styles = styles.chain_one(&barrier);
|
||||||
layouter.layout_par(world, node, styles)?;
|
layouter.layout_par(vt, node, styles)?;
|
||||||
} else if child.has::<dyn Layout>() {
|
} else if child.has::<dyn Layout>() {
|
||||||
layouter.layout_block(world, child, styles)?;
|
layouter.layout_block(vt, child, styles)?;
|
||||||
} else if child.is::<ColbreakNode>() {
|
} else if child.is::<ColbreakNode>() {
|
||||||
layouter.finish_region(false);
|
layouter.finish_region(false);
|
||||||
} else {
|
} else {
|
||||||
@ -122,16 +122,23 @@ impl FlowLayouter {
|
|||||||
/// Layout a paragraph.
|
/// Layout a paragraph.
|
||||||
fn layout_par(
|
fn layout_par(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
par: &ParNode,
|
par: &ParNode,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let aligns = Axes::new(styles.get(ParNode::ALIGN), Align::Top);
|
let aligns = Axes::new(styles.get(ParNode::ALIGN), Align::Top);
|
||||||
let leading = styles.get(ParNode::LEADING);
|
let leading = styles.get(ParNode::LEADING);
|
||||||
let consecutive = self.last_was_par;
|
let consecutive = self.last_was_par;
|
||||||
let fragment = par.layout(world, styles, &self.regions, consecutive)?;
|
let fragment = par.layout(
|
||||||
let len = fragment.len();
|
vt,
|
||||||
|
styles,
|
||||||
|
consecutive,
|
||||||
|
self.regions.first.x,
|
||||||
|
self.regions.base,
|
||||||
|
self.regions.expand.x,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let len = fragment.len();
|
||||||
for (i, frame) in fragment.into_iter().enumerate() {
|
for (i, frame) in fragment.into_iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
self.layout_item(FlowItem::Leading(leading));
|
self.layout_item(FlowItem::Leading(leading));
|
||||||
@ -151,7 +158,7 @@ impl FlowLayouter {
|
|||||||
/// Layout a block.
|
/// Layout a block.
|
||||||
fn layout_block(
|
fn layout_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
block: &Content,
|
block: &Content,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
@ -159,7 +166,7 @@ impl FlowLayouter {
|
|||||||
// aligned later.
|
// aligned later.
|
||||||
if let Some(placed) = block.to::<PlaceNode>() {
|
if let Some(placed) = block.to::<PlaceNode>() {
|
||||||
if placed.out_of_flow() {
|
if placed.out_of_flow() {
|
||||||
let frame = block.layout(world, styles, &self.regions)?.into_frame();
|
let frame = block.layout(vt, styles, &self.regions)?.into_frame();
|
||||||
self.layout_item(FlowItem::Placed(frame));
|
self.layout_item(FlowItem::Placed(frame));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -180,7 +187,7 @@ impl FlowLayouter {
|
|||||||
|
|
||||||
// Layout the block itself.
|
// Layout the block itself.
|
||||||
let sticky = styles.get(BlockNode::STICKY);
|
let sticky = styles.get(BlockNode::STICKY);
|
||||||
let fragment = block.layout(world, styles, &self.regions)?;
|
let fragment = block.layout(vt, styles, &self.regions)?;
|
||||||
for frame in fragment {
|
for frame in fragment {
|
||||||
self.layout_item(FlowItem::Frame(frame, aligns, sticky));
|
self.layout_item(FlowItem::Frame(frame, aligns, sticky));
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ impl GridNode {
|
|||||||
impl Layout for GridNode {
|
impl Layout for GridNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// 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(
|
||||||
world,
|
vt,
|
||||||
self.tracks.as_deref(),
|
self.tracks.as_deref(),
|
||||||
self.gutter.as_deref(),
|
self.gutter.as_deref(),
|
||||||
&self.cells,
|
&self.cells,
|
||||||
@ -110,9 +110,9 @@ castable! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Performs grid layout.
|
/// Performs grid layout.
|
||||||
struct GridLayouter<'a> {
|
struct GridLayouter<'a, 'v> {
|
||||||
/// The core context.
|
/// The core context.
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &'a mut Vt<'v>,
|
||||||
/// The grid cells.
|
/// The grid cells.
|
||||||
cells: &'a [Content],
|
cells: &'a [Content],
|
||||||
/// The column tracks including gutter tracks.
|
/// The column tracks including gutter tracks.
|
||||||
@ -147,12 +147,12 @@ enum Row {
|
|||||||
Fr(Fr, usize),
|
Fr(Fr, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GridLayouter<'a> {
|
impl<'a, 'v> GridLayouter<'a, 'v> {
|
||||||
/// Create a new grid layouter.
|
/// Create a new grid layouter.
|
||||||
///
|
///
|
||||||
/// This prepares grid layout by unifying content and gutter tracks.
|
/// This prepares grid layout by unifying content and gutter tracks.
|
||||||
fn new(
|
fn new(
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &'a mut Vt<'v>,
|
||||||
tracks: Axes<&[TrackSizing]>,
|
tracks: Axes<&[TrackSizing]>,
|
||||||
gutter: Axes<&[TrackSizing]>,
|
gutter: Axes<&[TrackSizing]>,
|
||||||
cells: &'a [Content],
|
cells: &'a [Content],
|
||||||
@ -206,7 +206,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
regions.expand = Axes::new(true, false);
|
regions.expand = Axes::new(true, false);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
world,
|
vt,
|
||||||
cells,
|
cells,
|
||||||
cols,
|
cols,
|
||||||
rows,
|
rows,
|
||||||
@ -318,7 +318,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
v.resolve(self.styles).relative_to(self.regions.base.y);
|
v.resolve(self.styles).relative_to(self.regions.base.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = cell.layout(self.world, self.styles, &pod)?.into_frame();
|
let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame();
|
||||||
resolved.set_max(frame.width());
|
resolved.set_max(frame.width());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -395,7 +395,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut sizes = cell
|
let mut sizes = cell
|
||||||
.layout(self.world, self.styles, &pod)?
|
.layout(self.vt, self.styles, &pod)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|frame| frame.height());
|
.map(|frame| frame.height());
|
||||||
|
|
||||||
@ -483,7 +483,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
.select(self.regions.base, size);
|
.select(self.regions.base, size);
|
||||||
|
|
||||||
let pod = Regions::one(size, base, Axes::splat(true));
|
let pod = Regions::one(size, base, Axes::splat(true));
|
||||||
let frame = cell.layout(self.world, self.styles, &pod)?.into_frame();
|
let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame();
|
||||||
output.push_frame(pos, frame);
|
output.push_frame(pos, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +519,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the layouted frames into the individual output frames.
|
// Push the layouted frames into the individual output frames.
|
||||||
let fragment = cell.layout(self.world, self.styles, &pod)?;
|
let fragment = cell.layout(self.vt, self.styles, &pod)?;
|
||||||
for (output, frame) in outputs.iter_mut().zip(fragment) {
|
for (output, frame) in outputs.iter_mut().zip(fragment) {
|
||||||
output.push_frame(pos, frame);
|
output.push_frame(pos, frame);
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@ impl HideNode {
|
|||||||
impl Layout for HideNode {
|
impl Layout for HideNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut fragment = self.0.layout(world, styles, regions)?;
|
let mut fragment = self.0.layout(vt, styles, regions)?;
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
frame.clear();
|
frame.clear();
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ pub use self::transform::*;
|
|||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use comemo::Tracked;
|
|
||||||
use typed_arena::Arena;
|
use typed_arena::Arena;
|
||||||
use typst::diag::SourceResult;
|
use typst::diag::SourceResult;
|
||||||
use typst::geom::*;
|
use typst::geom::*;
|
||||||
@ -40,7 +39,6 @@ use typst::model::{
|
|||||||
applicable, capability, realize, Content, Node, SequenceNode, Style, StyleChain,
|
applicable, capability, realize, Content, Node, SequenceNode, Style, StyleChain,
|
||||||
StyleVecBuilder, StyledNode,
|
StyleVecBuilder, StyledNode,
|
||||||
};
|
};
|
||||||
use typst::World;
|
|
||||||
|
|
||||||
use crate::basics::{DescNode, EnumNode, ListItem, ListNode, DESC, ENUM, LIST};
|
use crate::basics::{DescNode, EnumNode, ListItem, ListNode, DESC, ENUM, LIST};
|
||||||
use crate::meta::DocumentNode;
|
use crate::meta::DocumentNode;
|
||||||
@ -52,23 +50,27 @@ use crate::text::{LinebreakNode, SmartQuoteNode, SpaceNode, TextNode};
|
|||||||
#[capability]
|
#[capability]
|
||||||
pub trait LayoutRoot {
|
pub trait LayoutRoot {
|
||||||
/// Layout into one frame per page.
|
/// Layout into one frame per page.
|
||||||
fn layout_root(
|
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document>;
|
||||||
&self,
|
|
||||||
world: Tracked<dyn World>,
|
|
||||||
styles: StyleChain,
|
|
||||||
) -> SourceResult<Document>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutRoot for Content {
|
impl LayoutRoot for Content {
|
||||||
|
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
fn layout_root(
|
fn cached(
|
||||||
&self,
|
node: &Content,
|
||||||
world: Tracked<dyn World>,
|
world: comemo::Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Document> {
|
) -> SourceResult<Document> {
|
||||||
|
let mut vt = Vt { world };
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
let (realized, styles) = realize_root(world, &scratch, self, styles)?;
|
let (realized, styles) = realize_root(&mut vt, &scratch, node, styles)?;
|
||||||
realized.with::<dyn LayoutRoot>().unwrap().layout_root(world, styles)
|
realized
|
||||||
|
.with::<dyn LayoutRoot>()
|
||||||
|
.unwrap()
|
||||||
|
.layout_root(&mut vt, styles)
|
||||||
|
}
|
||||||
|
|
||||||
|
cached(self, vt.world, styles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,25 +80,38 @@ pub trait Layout {
|
|||||||
/// Layout into one frame per region.
|
/// Layout into one frame per region.
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment>;
|
) -> SourceResult<Fragment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for Content {
|
impl Layout for Content {
|
||||||
#[comemo::memoize]
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
|
#[comemo::memoize]
|
||||||
|
fn cached(
|
||||||
|
node: &Content,
|
||||||
|
world: comemo::Tracked<dyn World>,
|
||||||
|
styles: StyleChain,
|
||||||
|
regions: &Regions,
|
||||||
|
) -> SourceResult<Fragment> {
|
||||||
|
let mut vt = Vt { world };
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
let (realized, styles) = realize_block(world, &scratch, self, styles)?;
|
let (realized, styles) = realize_block(&mut vt, &scratch, node, styles)?;
|
||||||
let barrier = Style::Barrier(realized.id());
|
let barrier = Style::Barrier(realized.id());
|
||||||
let styles = styles.chain_one(&barrier);
|
let styles = styles.chain_one(&barrier);
|
||||||
realized.with::<dyn Layout>().unwrap().layout(world, styles, regions)
|
realized
|
||||||
|
.with::<dyn Layout>()
|
||||||
|
.unwrap()
|
||||||
|
.layout(&mut vt, styles, regions)
|
||||||
|
}
|
||||||
|
|
||||||
|
cached(self, vt.world, styles, regions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +214,7 @@ impl Regions {
|
|||||||
|
|
||||||
/// Realize into a node that is capable of root-level layout.
|
/// Realize into a node that is capable of root-level layout.
|
||||||
fn realize_root<'a>(
|
fn realize_root<'a>(
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &mut Vt,
|
||||||
scratch: &'a Scratch<'a>,
|
scratch: &'a Scratch<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
@ -208,7 +223,7 @@ fn realize_root<'a>(
|
|||||||
return Ok((content.clone(), styles));
|
return Ok((content.clone(), styles));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut builder = Builder::new(world, &scratch, true);
|
let mut builder = Builder::new(vt, &scratch, true);
|
||||||
builder.accept(content, styles)?;
|
builder.accept(content, styles)?;
|
||||||
builder.interrupt_page(Some(styles))?;
|
builder.interrupt_page(Some(styles))?;
|
||||||
let (pages, shared) = builder.doc.unwrap().pages.finish();
|
let (pages, shared) = builder.doc.unwrap().pages.finish();
|
||||||
@ -217,7 +232,7 @@ fn realize_root<'a>(
|
|||||||
|
|
||||||
/// Realize into a node that is capable of block-level layout.
|
/// Realize into a node that is capable of block-level layout.
|
||||||
fn realize_block<'a>(
|
fn realize_block<'a>(
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &mut Vt,
|
||||||
scratch: &'a Scratch<'a>,
|
scratch: &'a Scratch<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
@ -226,7 +241,7 @@ fn realize_block<'a>(
|
|||||||
return Ok((content.clone(), styles));
|
return Ok((content.clone(), styles));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut builder = Builder::new(world, &scratch, false);
|
let mut builder = Builder::new(vt, &scratch, false);
|
||||||
builder.accept(content, styles)?;
|
builder.accept(content, styles)?;
|
||||||
builder.interrupt_par()?;
|
builder.interrupt_par()?;
|
||||||
let (children, shared) = builder.flow.0.finish();
|
let (children, shared) = builder.flow.0.finish();
|
||||||
@ -234,9 +249,9 @@ fn realize_block<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a document or a flow node from content.
|
/// Builds a document or a flow node from content.
|
||||||
struct Builder<'a> {
|
struct Builder<'a, 'v, 't> {
|
||||||
/// The core context.
|
/// The virtual typesetter.
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &'v mut Vt<'t>,
|
||||||
/// Scratch arenas for building.
|
/// Scratch arenas for building.
|
||||||
scratch: &'a Scratch<'a>,
|
scratch: &'a Scratch<'a>,
|
||||||
/// The current document building state.
|
/// The current document building state.
|
||||||
@ -258,10 +273,10 @@ struct Scratch<'a> {
|
|||||||
content: Arena<Content>,
|
content: Arena<Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
||||||
fn new(world: Tracked<'a, dyn World>, scratch: &'a Scratch<'a>, top: bool) -> Self {
|
fn new(vt: &'v mut Vt<'t>, scratch: &'a Scratch<'a>, top: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
world,
|
vt,
|
||||||
scratch,
|
scratch,
|
||||||
doc: top.then(|| DocBuilder::default()),
|
doc: top.then(|| DocBuilder::default()),
|
||||||
flow: FlowBuilder::default(),
|
flow: FlowBuilder::default(),
|
||||||
@ -286,7 +301,7 @@ impl<'a> Builder<'a> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(realized) = realize(self.world, content, styles)? {
|
if let Some(realized) = realize(self.vt, content, styles)? {
|
||||||
let stored = self.scratch.content.alloc(realized);
|
let stored = self.scratch.content.alloc(realized);
|
||||||
return self.accept(stored, styles);
|
return self.accept(stored, styles);
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,14 @@ impl PadNode {
|
|||||||
impl Layout for PadNode {
|
impl Layout for PadNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// Layout child into padded regions.
|
// Layout child into padded regions.
|
||||||
let padding = self.padding.resolve(styles);
|
let padding = self.padding.resolve(styles);
|
||||||
let pod = regions.map(|size| shrink(size, padding));
|
let pod = regions.map(|size| shrink(size, padding));
|
||||||
let mut fragment = self.child.layout(world, styles, &pod)?;
|
let mut fragment = self.child.layout(vt, styles, &pod)?;
|
||||||
|
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
// Apply the padding inversely such that the grown size padded
|
// Apply the padding inversely such that the grown size padded
|
||||||
|
@ -57,7 +57,7 @@ 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(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
mut page: usize,
|
mut page: usize,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -97,7 +97,7 @@ impl PageNode {
|
|||||||
|
|
||||||
// Layout the child.
|
// Layout the child.
|
||||||
let regions = Regions::repeat(size, size, size.map(Abs::is_finite));
|
let regions = Regions::repeat(size, size, size.map(Abs::is_finite));
|
||||||
let mut fragment = child.layout(world, styles, ®ions)?;
|
let mut fragment = child.layout(vt, styles, ®ions)?;
|
||||||
|
|
||||||
let header = styles.get(Self::HEADER);
|
let header = styles.get(Self::HEADER);
|
||||||
let footer = styles.get(Self::FOOTER);
|
let footer = styles.get(Self::FOOTER);
|
||||||
@ -116,9 +116,9 @@ impl PageNode {
|
|||||||
(foreground, Point::zero(), size),
|
(foreground, Point::zero(), size),
|
||||||
(background, Point::zero(), size),
|
(background, Point::zero(), size),
|
||||||
] {
|
] {
|
||||||
if let Some(content) = marginal.resolve(world, page)? {
|
if let Some(content) = marginal.resolve(vt, page)? {
|
||||||
let pod = Regions::one(area, area, Axes::splat(true));
|
let pod = Regions::one(area, area, Axes::splat(true));
|
||||||
let sub = content.layout(world, styles, &pod)?.into_frame();
|
let sub = content.layout(vt, styles, &pod)?.into_frame();
|
||||||
if std::ptr::eq(marginal, background) {
|
if std::ptr::eq(marginal, background) {
|
||||||
frame.prepend_frame(pos, sub);
|
frame.prepend_frame(pos, sub);
|
||||||
} else {
|
} else {
|
||||||
@ -169,17 +169,13 @@ pub enum Marginal {
|
|||||||
|
|
||||||
impl Marginal {
|
impl Marginal {
|
||||||
/// Resolve the marginal based on the page number.
|
/// Resolve the marginal based on the page number.
|
||||||
pub fn resolve(
|
pub fn resolve(&self, vt: &Vt, page: usize) -> SourceResult<Option<Content>> {
|
||||||
&self,
|
|
||||||
world: Tracked<dyn World>,
|
|
||||||
page: usize,
|
|
||||||
) -> SourceResult<Option<Content>> {
|
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::None => None,
|
Self::None => None,
|
||||||
Self::Content(content) => Some(content.clone()),
|
Self::Content(content) => Some(content.clone()),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
let args = Args::new(*span, [Value::Int(page as i64)]);
|
let args = Args::new(*span, [Value::Int(page as i64)]);
|
||||||
Some(func.call_detached(world, args)?.display())
|
Some(func.call_detached(vt.world(), args)?.display())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,27 +44,43 @@ impl ParNode {
|
|||||||
|
|
||||||
impl ParNode {
|
impl ParNode {
|
||||||
/// Layout the paragraph into a collection of lines.
|
/// Layout the paragraph into a collection of lines.
|
||||||
#[comemo::memoize]
|
|
||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
|
vt: &mut Vt,
|
||||||
|
styles: StyleChain,
|
||||||
|
consecutive: bool,
|
||||||
|
width: Abs,
|
||||||
|
base: Size,
|
||||||
|
expand: bool,
|
||||||
|
) -> SourceResult<Fragment> {
|
||||||
|
#[comemo::memoize]
|
||||||
|
fn cached(
|
||||||
|
par: &ParNode,
|
||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
|
||||||
consecutive: bool,
|
consecutive: bool,
|
||||||
|
width: Abs,
|
||||||
|
base: Size,
|
||||||
|
expand: bool,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
|
let mut vt = Vt { world };
|
||||||
|
|
||||||
// Collect all text into one string for BiDi analysis.
|
// Collect all text into one string for BiDi analysis.
|
||||||
let (text, segments) = collect(self, &styles, consecutive);
|
let (text, segments) = collect(par, &styles, consecutive);
|
||||||
|
|
||||||
// Perform BiDi analysis and then prepare paragraph layout by building a
|
// Perform BiDi analysis and then prepare paragraph layout by building a
|
||||||
// representation on which we can do line breaking without layouting
|
// representation on which we can do line breaking without layouting
|
||||||
// each and every line from scratch.
|
// each and every line from scratch.
|
||||||
let p = prepare(world, self, &text, segments, regions, styles)?;
|
let p = prepare(&mut vt, par, &text, segments, styles, width, base)?;
|
||||||
|
|
||||||
// Break the paragraph into lines.
|
// Break the paragraph into lines.
|
||||||
let lines = linebreak(&p, regions.first.x);
|
let lines = linebreak(&vt, &p, width);
|
||||||
|
|
||||||
// Stack the lines into one frame per region.
|
// Stack the lines into one frame per region.
|
||||||
finalize(&p, &lines, regions)
|
finalize(&mut vt, &p, &lines, width, base, expand)
|
||||||
|
}
|
||||||
|
|
||||||
|
cached(self, vt.world, styles, consecutive, width, base, expand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +159,6 @@ const NODE_REPLACE: char = '\u{FFFC}'; // Object Replacement Character
|
|||||||
/// Only when a line break falls onto a text index that is not safe-to-break per
|
/// Only when a line break falls onto a text index that is not safe-to-break per
|
||||||
/// rustybuzz, we have to reshape that portion.
|
/// rustybuzz, we have to reshape that portion.
|
||||||
struct Preparation<'a> {
|
struct Preparation<'a> {
|
||||||
/// The compilation environment.
|
|
||||||
world: Tracked<'a, dyn World>,
|
|
||||||
/// Bidirectional text embedding levels for the paragraph.
|
/// Bidirectional text embedding levels for the paragraph.
|
||||||
bidi: BidiInfo<'a>,
|
bidi: BidiInfo<'a>,
|
||||||
/// Text runs, spacing and layouted nodes.
|
/// Text runs, spacing and layouted nodes.
|
||||||
@ -470,12 +484,13 @@ fn collect<'a>(
|
|||||||
/// Prepare paragraph layout by shaping the whole paragraph and layouting all
|
/// Prepare paragraph layout by shaping the whole paragraph and layouting all
|
||||||
/// contained inline-level content.
|
/// contained inline-level content.
|
||||||
fn prepare<'a>(
|
fn prepare<'a>(
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &mut Vt,
|
||||||
par: &'a ParNode,
|
par: &'a ParNode,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
|
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
|
||||||
regions: &Regions,
|
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
|
width: Abs,
|
||||||
|
base: Size,
|
||||||
) -> SourceResult<Preparation<'a>> {
|
) -> SourceResult<Preparation<'a>> {
|
||||||
let bidi = BidiInfo::new(
|
let bidi = BidiInfo::new(
|
||||||
text,
|
text,
|
||||||
@ -494,11 +509,11 @@ fn prepare<'a>(
|
|||||||
let end = cursor + segment.len();
|
let end = cursor + segment.len();
|
||||||
match segment {
|
match segment {
|
||||||
Segment::Text(_) => {
|
Segment::Text(_) => {
|
||||||
shape_range(&mut items, world, &bidi, cursor..end, styles);
|
shape_range(&mut items, vt, &bidi, cursor..end, styles);
|
||||||
}
|
}
|
||||||
Segment::Spacing(spacing) => match spacing {
|
Segment::Spacing(spacing) => match spacing {
|
||||||
Spacing::Relative(v) => {
|
Spacing::Relative(v) => {
|
||||||
let resolved = v.resolve(styles).relative_to(regions.base.x);
|
let resolved = v.resolve(styles).relative_to(base.x);
|
||||||
items.push(Item::Absolute(resolved));
|
items.push(Item::Absolute(resolved));
|
||||||
}
|
}
|
||||||
Spacing::Fractional(v) => {
|
Spacing::Fractional(v) => {
|
||||||
@ -509,9 +524,9 @@ fn prepare<'a>(
|
|||||||
if let Some(repeat) = inline.to::<RepeatNode>() {
|
if let Some(repeat) = inline.to::<RepeatNode>() {
|
||||||
items.push(Item::Repeat(repeat, styles));
|
items.push(Item::Repeat(repeat, styles));
|
||||||
} else {
|
} else {
|
||||||
let size = Size::new(regions.first.x, regions.base.y);
|
let size = Size::new(width, base.y);
|
||||||
let pod = Regions::one(size, regions.base, Axes::splat(false));
|
let pod = Regions::one(size, base, Axes::splat(false));
|
||||||
let mut frame = inline.layout(world, styles, &pod)?.into_frame();
|
let mut frame = inline.layout(vt, styles, &pod)?.into_frame();
|
||||||
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
|
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
|
||||||
items.push(Item::Frame(frame));
|
items.push(Item::Frame(frame));
|
||||||
}
|
}
|
||||||
@ -522,7 +537,6 @@ fn prepare<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Preparation {
|
Ok(Preparation {
|
||||||
world,
|
|
||||||
bidi,
|
bidi,
|
||||||
items,
|
items,
|
||||||
styles,
|
styles,
|
||||||
@ -537,14 +551,14 @@ fn prepare<'a>(
|
|||||||
/// items for them.
|
/// items for them.
|
||||||
fn shape_range<'a>(
|
fn shape_range<'a>(
|
||||||
items: &mut Vec<Item<'a>>,
|
items: &mut Vec<Item<'a>>,
|
||||||
world: Tracked<dyn World>,
|
vt: &Vt,
|
||||||
bidi: &BidiInfo<'a>,
|
bidi: &BidiInfo<'a>,
|
||||||
range: Range,
|
range: Range,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) {
|
) {
|
||||||
let mut process = |text, level: BidiLevel| {
|
let mut process = |text, level: BidiLevel| {
|
||||||
let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL };
|
let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL };
|
||||||
let shaped = shape(world, text, styles, dir);
|
let shaped = shape(vt, text, styles, dir);
|
||||||
items.push(Item::Text(shaped));
|
items.push(Item::Text(shaped));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -601,7 +615,7 @@ fn shared_get<'a, K: Key>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find suitable linebreaks.
|
/// Find suitable linebreaks.
|
||||||
fn linebreak<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
||||||
let linebreaks = p.styles.get(ParNode::LINEBREAKS).unwrap_or_else(|| {
|
let linebreaks = p.styles.get(ParNode::LINEBREAKS).unwrap_or_else(|| {
|
||||||
if p.styles.get(ParNode::JUSTIFY) {
|
if p.styles.get(ParNode::JUSTIFY) {
|
||||||
Linebreaks::Optimized
|
Linebreaks::Optimized
|
||||||
@ -611,22 +625,22 @@ fn linebreak<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
match linebreaks {
|
match linebreaks {
|
||||||
Linebreaks::Simple => linebreak_simple(p, width),
|
Linebreaks::Simple => linebreak_simple(vt, p, width),
|
||||||
Linebreaks::Optimized => linebreak_optimized(p, width),
|
Linebreaks::Optimized => linebreak_optimized(vt, p, width),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform line breaking in simple first-fit style. This means that we build
|
/// Perform line breaking in simple first-fit style. This means that we build
|
||||||
/// lines greedily, always taking the longest possible line. This may lead to
|
/// lines greedily, always taking the longest possible line. This may lead to
|
||||||
/// very unbalanced line, but is fast and simple.
|
/// very unbalanced line, but is fast and simple.
|
||||||
fn linebreak_simple<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
||||||
let mut lines = vec![];
|
let mut lines = vec![];
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let mut last = None;
|
let mut last = None;
|
||||||
|
|
||||||
for (end, mandatory, hyphen) in breakpoints(p) {
|
for (end, mandatory, hyphen) in breakpoints(p) {
|
||||||
// Compute the line and its size.
|
// Compute the line and its size.
|
||||||
let mut attempt = line(p, start..end, mandatory, hyphen);
|
let mut attempt = line(vt, p, start..end, mandatory, hyphen);
|
||||||
|
|
||||||
// If the line doesn't fit anymore, we push the last fitting attempt
|
// If the line doesn't fit anymore, we push the last fitting attempt
|
||||||
// into the stack and rebuild the line from the attempt's end. The
|
// into the stack and rebuild the line from the attempt's end. The
|
||||||
@ -635,7 +649,7 @@ fn linebreak_simple<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
|||||||
if let Some((last_attempt, last_end)) = last.take() {
|
if let Some((last_attempt, last_end)) = last.take() {
|
||||||
lines.push(last_attempt);
|
lines.push(last_attempt);
|
||||||
start = last_end;
|
start = last_end;
|
||||||
attempt = line(p, start..end, mandatory, hyphen);
|
attempt = line(vt, p, start..end, mandatory, hyphen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,7 +689,7 @@ fn linebreak_simple<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
|||||||
/// computed and stored in dynamic programming table) is minimal. The final
|
/// computed and stored in dynamic programming table) is minimal. The final
|
||||||
/// result is simply the layout determined for the last breakpoint at the end of
|
/// result is simply the layout determined for the last breakpoint at the end of
|
||||||
/// text.
|
/// text.
|
||||||
fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
|
||||||
/// The cost of a line or paragraph layout.
|
/// The cost of a line or paragraph layout.
|
||||||
type Cost = f64;
|
type Cost = f64;
|
||||||
|
|
||||||
@ -698,7 +712,7 @@ fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>>
|
|||||||
let mut table = vec![Entry {
|
let mut table = vec![Entry {
|
||||||
pred: 0,
|
pred: 0,
|
||||||
total: 0.0,
|
total: 0.0,
|
||||||
line: line(p, 0..0, false, false),
|
line: line(vt, p, 0..0, false, false),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let em = p.styles.get(TextNode::SIZE);
|
let em = p.styles.get(TextNode::SIZE);
|
||||||
@ -712,7 +726,7 @@ fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>>
|
|||||||
for (i, pred) in table.iter_mut().enumerate().skip(active) {
|
for (i, pred) in table.iter_mut().enumerate().skip(active) {
|
||||||
// Layout the line.
|
// Layout the line.
|
||||||
let start = pred.line.end;
|
let start = pred.line.end;
|
||||||
let attempt = line(p, start..end, mandatory, hyphen);
|
let attempt = line(vt, p, start..end, mandatory, hyphen);
|
||||||
|
|
||||||
// Determine how much the line's spaces would need to be stretched
|
// Determine how much the line's spaces would need to be stretched
|
||||||
// to make it the desired width.
|
// to make it the desired width.
|
||||||
@ -787,7 +801,7 @@ fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>>
|
|||||||
/// Returns for each breakpoint the text index, whether the break is mandatory
|
/// Returns for each breakpoint the text index, whether the break is mandatory
|
||||||
/// (after `\n`) and whether a hyphen is required (when breaking inside of a
|
/// (after `\n`) and whether a hyphen is required (when breaking inside of a
|
||||||
/// word).
|
/// word).
|
||||||
fn breakpoints<'a>(p: &'a Preparation) -> Breakpoints<'a> {
|
fn breakpoints<'a>(p: &'a Preparation<'a>) -> Breakpoints<'a> {
|
||||||
Breakpoints {
|
Breakpoints {
|
||||||
p,
|
p,
|
||||||
linebreaks: LineBreakIterator::new(p.bidi.text),
|
linebreaks: LineBreakIterator::new(p.bidi.text),
|
||||||
@ -885,6 +899,7 @@ impl Breakpoints<'_> {
|
|||||||
|
|
||||||
/// Create a line which spans the given range.
|
/// Create a line which spans the given range.
|
||||||
fn line<'a>(
|
fn line<'a>(
|
||||||
|
vt: &Vt,
|
||||||
p: &'a Preparation,
|
p: &'a Preparation,
|
||||||
mut range: Range,
|
mut range: Range,
|
||||||
mandatory: bool,
|
mandatory: bool,
|
||||||
@ -940,9 +955,9 @@ fn line<'a>(
|
|||||||
if hyphen || start + shaped.text.len() > range.end {
|
if hyphen || start + shaped.text.len() > range.end {
|
||||||
if hyphen || start < range.end || before.is_empty() {
|
if hyphen || start < range.end || before.is_empty() {
|
||||||
let shifted = start - base..range.end - base;
|
let shifted = start - base..range.end - base;
|
||||||
let mut reshaped = shaped.reshape(p.world, shifted);
|
let mut reshaped = shaped.reshape(vt, shifted);
|
||||||
if hyphen || shy {
|
if hyphen || shy {
|
||||||
reshaped.push_hyphen(p.world);
|
reshaped.push_hyphen(vt);
|
||||||
}
|
}
|
||||||
width += reshaped.width;
|
width += reshaped.width;
|
||||||
last = Some(Item::Text(reshaped));
|
last = Some(Item::Text(reshaped));
|
||||||
@ -963,7 +978,7 @@ fn line<'a>(
|
|||||||
if range.start + shaped.text.len() > end {
|
if range.start + shaped.text.len() > end {
|
||||||
if range.start < end {
|
if range.start < end {
|
||||||
let shifted = range.start - base..end - base;
|
let shifted = range.start - base..end - base;
|
||||||
let reshaped = shaped.reshape(p.world, shifted);
|
let reshaped = shaped.reshape(vt, shifted);
|
||||||
width += reshaped.width;
|
width += reshaped.width;
|
||||||
first = Some(Item::Text(reshaped));
|
first = Some(Item::Text(reshaped));
|
||||||
}
|
}
|
||||||
@ -992,27 +1007,35 @@ fn line<'a>(
|
|||||||
|
|
||||||
/// Combine layouted lines into one frame per region.
|
/// Combine layouted lines into one frame per region.
|
||||||
fn finalize(
|
fn finalize(
|
||||||
|
vt: &mut Vt,
|
||||||
p: &Preparation,
|
p: &Preparation,
|
||||||
lines: &[Line],
|
lines: &[Line],
|
||||||
regions: &Regions,
|
mut width: Abs,
|
||||||
|
base: Size,
|
||||||
|
expand: bool,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// Determine the paragraph's width: Full width of the region if we
|
// Determine the paragraph's width: Full width of the region if we
|
||||||
// should expand or there's fractional spacing, fit-to-width otherwise.
|
// should expand or there's fractional spacing, fit-to-width otherwise.
|
||||||
let mut width = regions.first.x;
|
if !expand && lines.iter().all(|line| line.fr().is_zero()) {
|
||||||
if !regions.expand.x && lines.iter().all(|line| line.fr().is_zero()) {
|
|
||||||
width = lines.iter().map(|line| line.width).max().unwrap_or_default();
|
width = lines.iter().map(|line| line.width).max().unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stack the lines into one frame per region.
|
// Stack the lines into one frame per region.
|
||||||
lines
|
lines
|
||||||
.iter()
|
.iter()
|
||||||
.map(|line| commit(p, line, regions.base, width))
|
.map(|line| commit(vt, p, line, base, width))
|
||||||
.collect::<SourceResult<_>>()
|
.collect::<SourceResult<_>>()
|
||||||
.map(Fragment::frames)
|
.map(Fragment::frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit to a line and build its frame.
|
/// Commit to a line and build its frame.
|
||||||
fn commit(p: &Preparation, line: &Line, base: Size, width: Abs) -> SourceResult<Frame> {
|
fn commit(
|
||||||
|
vt: &mut Vt,
|
||||||
|
p: &Preparation,
|
||||||
|
line: &Line,
|
||||||
|
base: Size,
|
||||||
|
width: Abs,
|
||||||
|
) -> SourceResult<Frame> {
|
||||||
let mut remaining = width - line.width;
|
let mut remaining = width - line.width;
|
||||||
let mut offset = Abs::zero();
|
let mut offset = Abs::zero();
|
||||||
|
|
||||||
@ -1079,7 +1102,7 @@ fn commit(p: &Preparation, line: &Line, base: Size, width: Abs) -> SourceResult<
|
|||||||
offset += v.share(fr, remaining);
|
offset += v.share(fr, remaining);
|
||||||
}
|
}
|
||||||
Item::Text(shaped) => {
|
Item::Text(shaped) => {
|
||||||
let frame = shaped.build(p.world, justification);
|
let frame = shaped.build(vt, justification);
|
||||||
push(&mut offset, frame);
|
push(&mut offset, frame);
|
||||||
}
|
}
|
||||||
Item::Frame(frame) => {
|
Item::Frame(frame) => {
|
||||||
@ -1090,7 +1113,7 @@ fn commit(p: &Preparation, line: &Line, base: Size, width: Abs) -> SourceResult<
|
|||||||
let fill = Fr::one().share(fr, remaining);
|
let fill = Fr::one().share(fr, remaining);
|
||||||
let size = Size::new(fill, base.y);
|
let size = Size::new(fill, base.y);
|
||||||
let pod = Regions::one(size, base, Axes::new(false, false));
|
let pod = Regions::one(size, base, Axes::new(false, false));
|
||||||
let frame = repeat.layout(p.world, *styles, &pod)?.into_frame();
|
let frame = repeat.layout(vt, *styles, &pod)?.into_frame();
|
||||||
let width = frame.width();
|
let width = frame.width();
|
||||||
let count = (fill / width).floor();
|
let count = (fill / width).floor();
|
||||||
let remaining = fill % width;
|
let remaining = fill % width;
|
||||||
|
@ -19,7 +19,7 @@ impl PlaceNode {
|
|||||||
impl Layout for PlaceNode {
|
impl Layout for PlaceNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -33,7 +33,7 @@ impl Layout for PlaceNode {
|
|||||||
Regions::one(regions.base, regions.base, expand)
|
Regions::one(regions.base, regions.base, expand)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut frame = self.0.layout(world, styles, &pod)?.into_frame();
|
let mut frame = self.0.layout(vt, styles, &pod)?.into_frame();
|
||||||
|
|
||||||
// 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
|
||||||
// space in our parent. Otherwise, respect the expand settings.
|
// space in our parent. Otherwise, respect the expand settings.
|
||||||
|
@ -14,11 +14,11 @@ impl RepeatNode {
|
|||||||
impl Layout for RepeatNode {
|
impl Layout for RepeatNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
self.0.layout(world, styles, regions)
|
self.0.layout(vt, styles, regions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ impl StackNode {
|
|||||||
impl Layout for StackNode {
|
impl Layout for StackNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -49,7 +49,7 @@ impl Layout for StackNode {
|
|||||||
layouter.layout_spacing(kind);
|
layouter.layout_spacing(kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
layouter.layout_block(world, block, styles)?;
|
layouter.layout_block(vt, block, styles)?;
|
||||||
deferred = self.spacing;
|
deferred = self.spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
/// Layout an arbitrary block.
|
/// Layout an arbitrary block.
|
||||||
fn layout_block(
|
fn layout_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
block: &Content,
|
block: &Content,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
@ -195,7 +195,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
self.dir.start().into()
|
self.dir.start().into()
|
||||||
});
|
});
|
||||||
|
|
||||||
let fragment = block.layout(world, styles, &self.regions)?;
|
let fragment = block.layout(vt, styles, &self.regions)?;
|
||||||
let len = fragment.len();
|
let len = fragment.len();
|
||||||
for (i, frame) in fragment.into_iter().enumerate() {
|
for (i, frame) in fragment.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.
|
||||||
|
@ -27,11 +27,11 @@ impl MoveNode {
|
|||||||
impl Layout for MoveNode {
|
impl Layout for MoveNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut fragment = self.child.layout(world, styles, regions)?;
|
let mut fragment = self.child.layout(vt, styles, regions)?;
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
let delta = self.delta.resolve(styles);
|
let delta = self.delta.resolve(styles);
|
||||||
let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s));
|
let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s));
|
||||||
@ -85,11 +85,11 @@ impl<const T: TransformKind> TransformNode<T> {
|
|||||||
impl<const T: TransformKind> Layout for TransformNode<T> {
|
impl<const T: TransformKind> Layout for TransformNode<T> {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut fragment = self.child.layout(world, styles, regions)?;
|
let mut fragment = self.child.layout(vt, styles, regions)?;
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
|
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
|
||||||
let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s));
|
let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s));
|
||||||
|
@ -30,7 +30,7 @@ impl MathNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for MathNode {
|
impl Show for MathNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> Content {
|
||||||
let mut map = StyleMap::new();
|
let mut map = StyleMap::new();
|
||||||
map.set_family(FontFamily::new("NewComputerModernMath"), styles);
|
map.set_family(FontFamily::new("NewComputerModernMath"), styles);
|
||||||
|
|
||||||
@ -51,11 +51,11 @@ impl Show for MathNode {
|
|||||||
impl Layout for MathNode {
|
impl Layout for MathNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
_: &Regions,
|
_: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
layout_tex(world, &self.texify(), self.display, styles)
|
layout_tex(vt, &self.texify(), self.display, styles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,13 +35,14 @@ impl Texify for Content {
|
|||||||
|
|
||||||
/// Layout a TeX formula into a frame.
|
/// Layout a TeX formula into a frame.
|
||||||
pub fn layout_tex(
|
pub fn layout_tex(
|
||||||
world: Tracked<dyn World>,
|
vt: &Vt,
|
||||||
tex: &str,
|
tex: &str,
|
||||||
display: bool,
|
display: bool,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// Load the font.
|
// Load the font.
|
||||||
let variant = variant(styles);
|
let variant = variant(styles);
|
||||||
|
let world = vt.world();
|
||||||
let mut font = None;
|
let mut font = None;
|
||||||
for family in families(styles) {
|
for family in families(styles) {
|
||||||
font = world.book().select(family, variant).and_then(|id| world.font(id));
|
font = world.book().select(family, variant).and_then(|id| world.font(id));
|
||||||
|
@ -18,15 +18,11 @@ impl DocumentNode {
|
|||||||
|
|
||||||
impl LayoutRoot for DocumentNode {
|
impl LayoutRoot for DocumentNode {
|
||||||
/// Layout the document into a sequence of frames, one per page.
|
/// Layout the document into a sequence of frames, one per page.
|
||||||
fn layout_root(
|
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
|
||||||
&self,
|
|
||||||
world: Tracked<dyn World>,
|
|
||||||
styles: StyleChain,
|
|
||||||
) -> SourceResult<Document> {
|
|
||||||
let mut pages = vec![];
|
let mut pages = vec![];
|
||||||
for (page, map) in self.0.iter() {
|
for (page, map) in self.0.iter() {
|
||||||
let number = 1 + pages.len();
|
let number = 1 + pages.len();
|
||||||
let fragment = page.layout(world, number, styles.chain(map))?;
|
let fragment = page.layout(vt, number, styles.chain(map))?;
|
||||||
pages.extend(fragment);
|
pages.extend(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ impl LinkNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for LinkNode {
|
impl Show for LinkNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> Content {
|
||||||
self.body.clone()
|
self.body.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ impl RefNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for RefNode {
|
impl Show for RefNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> Content {
|
||||||
TextNode::packed(format_eco!("@{}", self.0))
|
TextNode::packed(format_eco!("@{}", self.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ pub use std::fmt::{self, Debug, Formatter};
|
|||||||
pub use std::num::NonZeroUsize;
|
pub use std::num::NonZeroUsize;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use comemo::Tracked;
|
pub use comemo::{Track, Tracked, TrackedMut};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::diag::{bail, error, with_alternative, At, SourceResult, StrResult};
|
pub use typst::diag::{bail, error, with_alternative, At, SourceResult, StrResult};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
@ -16,8 +16,8 @@ pub use typst::geom::*;
|
|||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::model::{
|
pub use typst::model::{
|
||||||
array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast,
|
array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast,
|
||||||
Content, Dict, Finalize, Fold, Func, Label, Node, NodeId, Resolve, Show, Smart, Str,
|
Content, Dict, Finalize, Fold, Func, Label, Node, NodeId, Resolve, Selector, Show,
|
||||||
StyleChain, StyleMap, StyleVec, Unlabellable, Value, Vm,
|
Smart, Str, StyleChain, StyleMap, StyleVec, Unlabellable, Value, Vm, Vt,
|
||||||
};
|
};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use typst::syntax::{Span, Spanned};
|
pub use typst::syntax::{Span, Spanned};
|
||||||
|
@ -105,11 +105,11 @@ impl FillNode {}
|
|||||||
impl Layout for FillNode {
|
impl Layout for FillNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut fragment = self.child.layout(world, styles, regions)?;
|
let mut fragment = self.child.layout(vt, styles, regions)?;
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
let shape = Geometry::Rect(frame.size()).filled(self.fill);
|
let shape = Geometry::Rect(frame.size()).filled(self.fill);
|
||||||
frame.prepend(Point::zero(), Element::Shape(shape));
|
frame.prepend(Point::zero(), Element::Shape(shape));
|
||||||
@ -133,11 +133,11 @@ impl StrokeNode {}
|
|||||||
impl Layout for StrokeNode {
|
impl Layout for StrokeNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut fragment = self.child.layout(world, styles, regions)?;
|
let mut fragment = self.child.layout(vt, styles, regions)?;
|
||||||
for frame in &mut fragment {
|
for frame in &mut fragment {
|
||||||
let shape = Geometry::Rect(frame.size()).stroked(self.stroke);
|
let shape = Geometry::Rect(frame.size()).stroked(self.stroke);
|
||||||
frame.prepend(Point::zero(), Element::Shape(shape));
|
frame.prepend(Point::zero(), Element::Shape(shape));
|
||||||
|
@ -47,7 +47,7 @@ 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, _: Tracked<dyn World>, styles: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> Content {
|
||||||
self.0.clone().styled(
|
self.0.clone().styled(
|
||||||
TextNode::DECO,
|
TextNode::DECO,
|
||||||
Decoration {
|
Decoration {
|
||||||
|
@ -62,7 +62,7 @@ impl StrongNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for StrongNode {
|
impl Show for StrongNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> Content {
|
||||||
self.0.clone().styled(TextNode::DELTA, Delta(styles.get(Self::DELTA)))
|
self.0.clone().styled(TextNode::DELTA, Delta(styles.get(Self::DELTA)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ impl EmphNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for EmphNode {
|
impl Show for EmphNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> Content {
|
||||||
self.0.clone().styled(TextNode::EMPH, Toggle)
|
self.0.clone().styled(TextNode::EMPH, Toggle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ impl RawNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Show for RawNode {
|
impl Show for RawNode {
|
||||||
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> Content {
|
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> Content {
|
||||||
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
|
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
|
||||||
let foreground = THEME
|
let foreground = THEME
|
||||||
.settings
|
.settings
|
||||||
|
@ -81,8 +81,8 @@ impl<'a> ShapedText<'a> {
|
|||||||
///
|
///
|
||||||
/// The `justification` defines how much extra advance width each
|
/// The `justification` defines how much extra advance width each
|
||||||
/// [justifiable glyph](ShapedGlyph::is_justifiable) will get.
|
/// [justifiable glyph](ShapedGlyph::is_justifiable) will get.
|
||||||
pub fn build(&self, world: Tracked<dyn World>, justification: Abs) -> Frame {
|
pub fn build(&self, vt: &Vt, justification: Abs) -> Frame {
|
||||||
let (top, bottom) = self.measure(world);
|
let (top, bottom) = self.measure(vt);
|
||||||
let size = Size::new(self.width, top + bottom);
|
let size = Size::new(self.width, top + bottom);
|
||||||
|
|
||||||
let mut offset = Abs::zero();
|
let mut offset = Abs::zero();
|
||||||
@ -137,7 +137,7 @@ impl<'a> ShapedText<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Measure the top and bottom extent of this text.
|
/// Measure the top and bottom extent of this text.
|
||||||
fn measure(&self, world: Tracked<dyn World>) -> (Abs, Abs) {
|
fn measure(&self, vt: &Vt) -> (Abs, Abs) {
|
||||||
let mut top = Abs::zero();
|
let mut top = Abs::zero();
|
||||||
let mut bottom = Abs::zero();
|
let mut bottom = Abs::zero();
|
||||||
|
|
||||||
@ -154,6 +154,7 @@ impl<'a> ShapedText<'a> {
|
|||||||
if self.glyphs.is_empty() {
|
if self.glyphs.is_empty() {
|
||||||
// When there are no glyphs, we just use the vertical metrics of the
|
// When there are no glyphs, we just use the vertical metrics of the
|
||||||
// first available font.
|
// first available font.
|
||||||
|
let world = vt.world();
|
||||||
for family in families(self.styles) {
|
for family in families(self.styles) {
|
||||||
if let Some(font) = world
|
if let Some(font) = world
|
||||||
.book()
|
.book()
|
||||||
@ -190,11 +191,7 @@ impl<'a> ShapedText<'a> {
|
|||||||
|
|
||||||
/// Reshape a range of the shaped text, reusing information from this
|
/// Reshape a range of the shaped text, reusing information from this
|
||||||
/// shaping process if possible.
|
/// shaping process if possible.
|
||||||
pub fn reshape(
|
pub fn reshape(&'a self, vt: &Vt, text_range: Range<usize>) -> ShapedText<'a> {
|
||||||
&'a self,
|
|
||||||
world: Tracked<dyn World>,
|
|
||||||
text_range: Range<usize>,
|
|
||||||
) -> ShapedText<'a> {
|
|
||||||
if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) {
|
if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) {
|
||||||
Self {
|
Self {
|
||||||
text: &self.text[text_range],
|
text: &self.text[text_range],
|
||||||
@ -206,13 +203,14 @@ impl<'a> ShapedText<'a> {
|
|||||||
glyphs: Cow::Borrowed(glyphs),
|
glyphs: Cow::Borrowed(glyphs),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shape(world, &self.text[text_range], self.styles, self.dir)
|
shape(vt, &self.text[text_range], self.styles, self.dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a hyphen to end of the text.
|
/// Push a hyphen to end of the text.
|
||||||
pub fn push_hyphen(&mut self, world: Tracked<dyn World>) {
|
pub fn push_hyphen(&mut self, vt: &Vt) {
|
||||||
families(self.styles).find_map(|family| {
|
families(self.styles).find_map(|family| {
|
||||||
|
let world = vt.world();
|
||||||
let font = world
|
let font = world
|
||||||
.book()
|
.book()
|
||||||
.select(family, self.variant)
|
.select(family, self.variant)
|
||||||
@ -303,7 +301,7 @@ impl Debug for ShapedText<'_> {
|
|||||||
|
|
||||||
/// Holds shaping results and metadata common to all shaped segments.
|
/// Holds shaping results and metadata common to all shaped segments.
|
||||||
struct ShapingContext<'a> {
|
struct ShapingContext<'a> {
|
||||||
world: Tracked<'a, dyn World>,
|
vt: &'a Vt<'a>,
|
||||||
glyphs: Vec<ShapedGlyph>,
|
glyphs: Vec<ShapedGlyph>,
|
||||||
used: Vec<Font>,
|
used: Vec<Font>,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
@ -316,7 +314,7 @@ struct ShapingContext<'a> {
|
|||||||
|
|
||||||
/// Shape text into [`ShapedText`].
|
/// Shape text into [`ShapedText`].
|
||||||
pub fn shape<'a>(
|
pub fn shape<'a>(
|
||||||
world: Tracked<dyn World>,
|
vt: &Vt,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
@ -324,7 +322,7 @@ pub fn shape<'a>(
|
|||||||
let size = styles.get(TextNode::SIZE);
|
let size = styles.get(TextNode::SIZE);
|
||||||
|
|
||||||
let mut ctx = ShapingContext {
|
let mut ctx = ShapingContext {
|
||||||
world,
|
vt,
|
||||||
size,
|
size,
|
||||||
glyphs: vec![],
|
glyphs: vec![],
|
||||||
used: vec![],
|
used: vec![],
|
||||||
@ -365,10 +363,11 @@ fn shape_segment<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the next available family.
|
// Find the next available family.
|
||||||
let book = ctx.world.book();
|
let world = ctx.vt.world();
|
||||||
|
let book = world.book();
|
||||||
let mut selection = families.find_map(|family| {
|
let mut selection = families.find_map(|family| {
|
||||||
book.select(family, ctx.variant)
|
book.select(family, ctx.variant)
|
||||||
.and_then(|id| ctx.world.font(id))
|
.and_then(|id| world.font(id))
|
||||||
.filter(|font| !ctx.used.contains(font))
|
.filter(|font| !ctx.used.contains(font))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -377,7 +376,7 @@ fn shape_segment<'a>(
|
|||||||
let first = ctx.used.first().map(Font::info);
|
let first = ctx.used.first().map(Font::info);
|
||||||
selection = book
|
selection = book
|
||||||
.select_fallback(first, ctx.variant, text)
|
.select_fallback(first, ctx.variant, text)
|
||||||
.and_then(|id| ctx.world.font(id))
|
.and_then(|id| world.font(id))
|
||||||
.filter(|font| !ctx.used.contains(font));
|
.filter(|font| !ctx.used.contains(font));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,11 +43,11 @@ impl<const S: ShiftKind> ShiftNode<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const S: ShiftKind> Show for ShiftNode<S> {
|
impl<const S: ShiftKind> Show for ShiftNode<S> {
|
||||||
fn show(&self, world: Tracked<dyn World>, styles: StyleChain) -> Content {
|
fn show(&self, vt: &mut Vt, _: &Content, styles: StyleChain) -> Content {
|
||||||
let mut transformed = None;
|
let mut transformed = None;
|
||||||
if styles.get(Self::TYPOGRAPHIC) {
|
if styles.get(Self::TYPOGRAPHIC) {
|
||||||
if let Some(text) = search_text(&self.0, S) {
|
if let Some(text) = search_text(&self.0, S) {
|
||||||
if is_shapable(world, &text, styles) {
|
if is_shapable(vt, &text, styles) {
|
||||||
transformed = Some(TextNode::packed(text));
|
transformed = Some(TextNode::packed(text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +85,8 @@ fn search_text(content: &Content, mode: ShiftKind) -> Option<EcoString> {
|
|||||||
|
|
||||||
/// Checks whether the first retrievable family contains all code points of the
|
/// Checks whether the first retrievable family contains all code points of the
|
||||||
/// given string.
|
/// given string.
|
||||||
fn is_shapable(world: Tracked<dyn World>, text: &str, styles: StyleChain) -> bool {
|
fn is_shapable(vt: &Vt, text: &str, styles: StyleChain) -> bool {
|
||||||
|
let world = vt.world();
|
||||||
for family in styles.get(TextNode::FAMILY).0.iter() {
|
for family in styles.get(TextNode::FAMILY).0.iter() {
|
||||||
if let Some(font) = world
|
if let Some(font) = world
|
||||||
.book()
|
.book()
|
||||||
|
@ -40,7 +40,7 @@ impl ImageNode {
|
|||||||
impl Layout for ImageNode {
|
impl Layout for ImageNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
|
@ -39,7 +39,7 @@ impl LineNode {
|
|||||||
impl Layout for LineNode {
|
impl Layout for LineNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
_: Tracked<dyn World>,
|
_: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
|
@ -75,7 +75,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
@ -90,7 +90,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
|
let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
|
||||||
|
|
||||||
let mut pod = Regions::one(regions.first, regions.base, regions.expand);
|
let mut pod = Regions::one(regions.first, regions.base, regions.expand);
|
||||||
frame = child.layout(world, styles, &pod)?.into_frame();
|
frame = child.layout(vt, styles, &pod)?.into_frame();
|
||||||
|
|
||||||
// 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.
|
||||||
@ -106,7 +106,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
|||||||
|
|
||||||
pod.first = Size::splat(length);
|
pod.first = Size::splat(length);
|
||||||
pod.expand = Axes::splat(true);
|
pod.expand = Axes::splat(true);
|
||||||
frame = child.layout(world, styles, &pod)?.into_frame();
|
frame = child.layout(vt, styles, &pod)?.into_frame();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The default size that a shape takes on if it has no child and
|
// The default size that a shape takes on if it has no child and
|
||||||
|
@ -2,15 +2,13 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
use comemo::Tracked;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
use super::{Content, NodeId, Scope, StyleChain, StyleMap};
|
use super::{Content, NodeId, Scope, StyleChain, StyleMap, Vt};
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::doc::Document;
|
use crate::doc::Document;
|
||||||
use crate::geom::{Abs, Dir};
|
use crate::geom::{Abs, Dir};
|
||||||
use crate::util::{hash128, EcoString};
|
use crate::util::{hash128, EcoString};
|
||||||
use crate::World;
|
|
||||||
|
|
||||||
/// Definition of Typst's standard library.
|
/// Definition of Typst's standard library.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
@ -27,11 +25,8 @@ pub struct Library {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LangItems {
|
pub struct LangItems {
|
||||||
/// The root layout function.
|
/// The root layout function.
|
||||||
pub layout: fn(
|
pub layout:
|
||||||
world: Tracked<dyn World>,
|
fn(vt: &mut Vt, content: &Content, styles: StyleChain) -> SourceResult<Document>,
|
||||||
content: &Content,
|
|
||||||
styles: StyleChain,
|
|
||||||
) -> SourceResult<Document>,
|
|
||||||
/// Access the em size.
|
/// Access the em size.
|
||||||
pub em: fn(StyleChain) -> Abs,
|
pub em: fn(StyleChain) -> Abs,
|
||||||
/// Access the text direction.
|
/// Access the text direction.
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use comemo::Tracked;
|
use super::{capability, Content, NodeId, Recipe, Selector, StyleChain, Vt};
|
||||||
|
|
||||||
use super::{capability, Content, NodeId, Recipe, Selector, StyleChain};
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::World;
|
|
||||||
|
|
||||||
/// Whether the target is affected by show rules in the given style chain.
|
/// Whether the target is affected by show rules in the given style chain.
|
||||||
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
||||||
@ -22,7 +19,7 @@ pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
|||||||
|
|
||||||
/// Apply the show rules in the given style chain to a target.
|
/// Apply the show rules in the given style chain to a target.
|
||||||
pub fn realize(
|
pub fn realize(
|
||||||
world: Tracked<dyn World>,
|
vt: &mut Vt,
|
||||||
target: &Content,
|
target: &Content,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Option<Content>> {
|
) -> SourceResult<Option<Content>> {
|
||||||
@ -34,7 +31,7 @@ pub fn realize(
|
|||||||
for recipe in styles.recipes() {
|
for recipe in styles.recipes() {
|
||||||
let guard = Guard::Nth(n);
|
let guard = Guard::Nth(n);
|
||||||
if recipe.applicable(target) && !target.is_guarded(guard) {
|
if recipe.applicable(target) && !target.is_guarded(guard) {
|
||||||
if let Some(content) = try_apply(world, &target, recipe, guard)? {
|
if let Some(content) = try_apply(vt, &target, recipe, guard)? {
|
||||||
realized = Some(content);
|
realized = Some(content);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -46,7 +43,7 @@ pub fn realize(
|
|||||||
if let Some(showable) = target.with::<dyn Show>() {
|
if let Some(showable) = target.with::<dyn Show>() {
|
||||||
let guard = Guard::Base(target.id());
|
let guard = Guard::Base(target.id());
|
||||||
if realized.is_none() && !target.is_guarded(guard) {
|
if realized.is_none() && !target.is_guarded(guard) {
|
||||||
realized = Some(showable.show(world, styles));
|
realized = Some(showable.show(vt, target, styles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +61,7 @@ pub fn realize(
|
|||||||
|
|
||||||
/// Try to apply a recipe to the target.
|
/// Try to apply a recipe to the target.
|
||||||
fn try_apply(
|
fn try_apply(
|
||||||
world: Tracked<dyn World>,
|
vt: &Vt,
|
||||||
target: &Content,
|
target: &Content,
|
||||||
recipe: &Recipe,
|
recipe: &Recipe,
|
||||||
guard: Guard,
|
guard: Guard,
|
||||||
@ -75,7 +72,7 @@ fn try_apply(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe.apply(world, target.clone().guarded(guard)).map(Some)
|
recipe.apply(vt.world(), target.clone().guarded(guard)).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Selector::Label(label)) => {
|
Some(Selector::Label(label)) => {
|
||||||
@ -83,7 +80,7 @@ fn try_apply(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe.apply(world, target.clone().guarded(guard)).map(Some)
|
recipe.apply(vt.world(), target.clone().guarded(guard)).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Selector::Regex(regex)) => {
|
Some(Selector::Regex(regex)) => {
|
||||||
@ -107,7 +104,7 @@ fn try_apply(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let piece = make(m.as_str().into()).guarded(guard);
|
let piece = make(m.as_str().into()).guarded(guard);
|
||||||
let transformed = recipe.apply(world, piece)?;
|
let transformed = recipe.apply(vt.world(), piece)?;
|
||||||
result.push(transformed);
|
result.push(transformed);
|
||||||
cursor = m.end();
|
cursor = m.end();
|
||||||
}
|
}
|
||||||
@ -131,7 +128,7 @@ fn try_apply(
|
|||||||
#[capability]
|
#[capability]
|
||||||
pub trait Show {
|
pub trait Show {
|
||||||
/// Execute the base recipe for this node.
|
/// Execute the base recipe for this node.
|
||||||
fn show(&self, world: Tracked<dyn World>, styles: StyleChain) -> Content;
|
fn show(&self, vt: &mut Vt, this: &Content, styles: StyleChain) -> Content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Post-process a node after it was realized.
|
/// Post-process a node after it was realized.
|
||||||
|
@ -10,5 +10,23 @@ use crate::World;
|
|||||||
pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Document> {
|
pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Document> {
|
||||||
let library = world.library();
|
let library = world.library();
|
||||||
let styles = StyleChain::new(&library.styles);
|
let styles = StyleChain::new(&library.styles);
|
||||||
(library.items.layout)(world, content, styles)
|
let mut vt = Vt { world };
|
||||||
|
(library.items.layout)(&mut vt, content, styles)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A virtual typesetter.
|
||||||
|
///
|
||||||
|
/// Holds the state needed to [typeset] content. This is the equivalent to the
|
||||||
|
/// [Vm](super::Vm) for typesetting.
|
||||||
|
pub struct Vt<'a> {
|
||||||
|
/// The compilation environment.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub world: Tracked<'a, dyn World>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Vt<'a> {
|
||||||
|
/// Access the underlying world.
|
||||||
|
pub fn world(&self) -> Tracked<'a, dyn World> {
|
||||||
|
self.world
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
typst = { path = ".." }
|
typst = { path = ".." }
|
||||||
typst-library = { path = "../library" }
|
typst-library = { path = "../library" }
|
||||||
comemo = "0.1"
|
comemo = { git = "https://github.com/typst/comemo" }
|
||||||
elsa = "1.7"
|
elsa = "1.7"
|
||||||
iai = { git = "https://github.com/reknih/iai" }
|
iai = { git = "https://github.com/reknih/iai" }
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user