mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Bring building a bit closer to realize
This commit is contained in:
parent
c2749f7615
commit
65aa27014d
@ -32,7 +32,8 @@ use typst::diag::SourceResult;
|
|||||||
use typst::frame::Frame;
|
use typst::frame::Frame;
|
||||||
use typst::geom::*;
|
use typst::geom::*;
|
||||||
use typst::model::{
|
use typst::model::{
|
||||||
capability, Content, SequenceNode, Style, StyleChain, StyleVecBuilder, StyledNode,
|
capability, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder,
|
||||||
|
StyledNode,
|
||||||
};
|
};
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
|
||||||
@ -64,10 +65,8 @@ impl LayoutRoot for Content {
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
let mut builder = Builder::new(world, &scratch, true);
|
let (realized, styles) = realize_root(world, &scratch, self, styles)?;
|
||||||
builder.accept(self, styles)?;
|
realized.with::<dyn LayoutRoot>().unwrap().layout_root(world, styles)
|
||||||
let (doc, shared) = builder.into_doc(styles)?;
|
|
||||||
doc.layout_root(world, shared)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,19 +90,14 @@ impl LayoutBlock for Content {
|
|||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<Vec<Frame>> {
|
) -> SourceResult<Vec<Frame>> {
|
||||||
if !styles.applicable(self) {
|
|
||||||
if let Some(node) = self.with::<dyn LayoutBlock>() {
|
|
||||||
let barrier = Style::Barrier(self.id());
|
|
||||||
let styles = barrier.chain(&styles);
|
|
||||||
return node.layout_block(world, regions, styles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scratch = Scratch::default();
|
let scratch = Scratch::default();
|
||||||
let mut builder = Builder::new(world, &scratch, false);
|
let (realized, styles) = realize_block(world, &scratch, self, styles)?;
|
||||||
builder.accept(self, styles)?;
|
let barrier = Style::Barrier(realized.id());
|
||||||
let (flow, shared) = builder.into_flow(styles)?;
|
let styles = barrier.chain(&styles);
|
||||||
flow.layout_block(world, regions, shared)
|
realized
|
||||||
|
.with::<dyn LayoutBlock>()
|
||||||
|
.unwrap()
|
||||||
|
.layout_block(world, regions, styles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,25 +124,16 @@ impl LayoutInline for Content {
|
|||||||
assert!(regions.backlog.is_empty());
|
assert!(regions.backlog.is_empty());
|
||||||
assert!(regions.last.is_none());
|
assert!(regions.last.is_none());
|
||||||
|
|
||||||
if !styles.applicable(self) {
|
if self.has::<dyn LayoutInline>() && !styles.applicable(self) {
|
||||||
if let Some(node) = self.with::<dyn LayoutInline>() {
|
|
||||||
let barrier = Style::Barrier(self.id());
|
let barrier = Style::Barrier(self.id());
|
||||||
let styles = barrier.chain(&styles);
|
let styles = barrier.chain(&styles);
|
||||||
return node.layout_inline(world, regions, styles);
|
return self
|
||||||
|
.with::<dyn LayoutInline>()
|
||||||
|
.unwrap()
|
||||||
|
.layout_inline(world, regions, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(node) = self.with::<dyn LayoutBlock>() {
|
Ok(self.layout_block(world, regions, styles)?.remove(0))
|
||||||
let barrier = Style::Barrier(self.id());
|
|
||||||
let styles = barrier.chain(&styles);
|
|
||||||
return Ok(node.layout_block(world, regions, styles)?.remove(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scratch = Scratch::default();
|
|
||||||
let mut builder = Builder::new(world, &scratch, false);
|
|
||||||
builder.accept(self, styles)?;
|
|
||||||
let (flow, shared) = builder.into_flow(styles)?;
|
|
||||||
Ok(flow.layout_block(world, regions, shared)?.remove(0))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +230,42 @@ impl Regions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Realize into a node that is capable of root-level layout.
|
||||||
|
fn realize_root<'a>(
|
||||||
|
world: Tracked<'a, dyn World>,
|
||||||
|
scratch: &'a Scratch<'a>,
|
||||||
|
content: &'a Content,
|
||||||
|
styles: StyleChain<'a>,
|
||||||
|
) -> SourceResult<(Content, StyleChain<'a>)> {
|
||||||
|
if content.has::<dyn LayoutRoot>() && !styles.applicable(content) {
|
||||||
|
return Ok((content.clone(), styles));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder = Builder::new(world, &scratch, true);
|
||||||
|
builder.accept(content, styles)?;
|
||||||
|
builder.interrupt_page(Some(styles))?;
|
||||||
|
let (pages, shared) = builder.doc.unwrap().pages.finish();
|
||||||
|
Ok((DocNode(pages).pack(), shared))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Realize into a node that is capable of block-level layout.
|
||||||
|
fn realize_block<'a>(
|
||||||
|
world: Tracked<'a, dyn World>,
|
||||||
|
scratch: &'a Scratch<'a>,
|
||||||
|
content: &'a Content,
|
||||||
|
styles: StyleChain<'a>,
|
||||||
|
) -> SourceResult<(Content, StyleChain<'a>)> {
|
||||||
|
if content.has::<dyn LayoutBlock>() && !styles.applicable(content) {
|
||||||
|
return Ok((content.clone(), styles));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder = Builder::new(world, &scratch, false);
|
||||||
|
builder.accept(content, styles)?;
|
||||||
|
builder.interrupt_par()?;
|
||||||
|
let (children, shared) = builder.flow.0.finish();
|
||||||
|
Ok((FlowNode(children).pack(), shared))
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a document or a flow node from content.
|
/// Builds a document or a flow node from content.
|
||||||
struct Builder<'a> {
|
struct Builder<'a> {
|
||||||
/// The core context.
|
/// The core context.
|
||||||
@ -270,17 +291,6 @@ struct Scratch<'a> {
|
|||||||
content: Arena<Content>,
|
content: Arena<Content>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether a style could interrupt some composable structure.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
||||||
enum Interruption {
|
|
||||||
/// The style forces a list break.
|
|
||||||
List,
|
|
||||||
/// The style forces a paragraph break.
|
|
||||||
Par,
|
|
||||||
/// The style forces a page break.
|
|
||||||
Page,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> Builder<'a> {
|
||||||
fn new(world: Tracked<'a, dyn World>, scratch: &'a Scratch<'a>, top: bool) -> Self {
|
fn new(world: Tracked<'a, dyn World>, scratch: &'a Scratch<'a>, top: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -293,24 +303,6 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_doc(
|
|
||||||
mut self,
|
|
||||||
styles: StyleChain<'a>,
|
|
||||||
) -> SourceResult<(DocNode, StyleChain<'a>)> {
|
|
||||||
self.interrupt(Interruption::Page, styles, true)?;
|
|
||||||
let (pages, shared) = self.doc.unwrap().pages.finish();
|
|
||||||
Ok((DocNode(pages), shared))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_flow(
|
|
||||||
mut self,
|
|
||||||
styles: StyleChain<'a>,
|
|
||||||
) -> SourceResult<(FlowNode, StyleChain<'a>)> {
|
|
||||||
self.interrupt(Interruption::Par, styles, false)?;
|
|
||||||
let (children, shared) = self.flow.0.finish();
|
|
||||||
Ok((FlowNode(children), shared))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accept(
|
fn accept(
|
||||||
&mut self,
|
&mut self,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
@ -321,7 +313,10 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(seq) = content.to::<SequenceNode>() {
|
if let Some(seq) = content.to::<SequenceNode>() {
|
||||||
return self.sequence(seq, styles);
|
for sub in &seq.0 {
|
||||||
|
self.accept(sub, styles)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(realized) = styles.show(self.world, content)? {
|
if let Some(realized) = styles.show(self.world, content)? {
|
||||||
@ -333,7 +328,7 @@ impl<'a> Builder<'a> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.interrupt(Interruption::List, styles, false)?;
|
self.interrupt_list()?;
|
||||||
|
|
||||||
if content.is::<ListItem>() {
|
if content.is::<ListItem>() {
|
||||||
self.list.accept(content, styles);
|
self.list.accept(content, styles);
|
||||||
@ -344,7 +339,7 @@ impl<'a> Builder<'a> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.interrupt(Interruption::Par, styles, false)?;
|
self.interrupt_par()?;
|
||||||
|
|
||||||
if self.flow.accept(content, styles) {
|
if self.flow.accept(content, styles) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -353,7 +348,8 @@ impl<'a> Builder<'a> {
|
|||||||
let keep = content
|
let keep = content
|
||||||
.to::<PagebreakNode>()
|
.to::<PagebreakNode>()
|
||||||
.map_or(false, |pagebreak| !pagebreak.weak);
|
.map_or(false, |pagebreak| !pagebreak.weak);
|
||||||
self.interrupt(Interruption::Page, styles, keep)?;
|
|
||||||
|
self.interrupt_page(keep.then(|| styles))?;
|
||||||
|
|
||||||
if let Some(doc) = &mut self.doc {
|
if let Some(doc) = &mut self.doc {
|
||||||
if doc.accept(content, styles) {
|
if doc.accept(content, styles) {
|
||||||
@ -361,9 +357,6 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We might want to issue a warning or error for content that wasn't
|
|
||||||
// handled (e.g. a pagebreak in a flow building process). However, we
|
|
||||||
// don't have the spans here at the moment.
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,40 +367,32 @@ impl<'a> Builder<'a> {
|
|||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
let stored = self.scratch.styles.alloc(styles);
|
let stored = self.scratch.styles.alloc(styles);
|
||||||
let styles = styled.map.chain(stored);
|
let styles = styled.map.chain(stored);
|
||||||
|
self.interrupt_style(&styled.map, None)?;
|
||||||
let intr = if styled.map.interrupts::<PageNode>() {
|
|
||||||
Some(Interruption::Page)
|
|
||||||
} else if styled.map.interrupts::<ParNode>() {
|
|
||||||
Some(Interruption::Par)
|
|
||||||
} else if styled.map.interrupts::<ListNode>()
|
|
||||||
|| styled.map.interrupts::<EnumNode>()
|
|
||||||
|| styled.map.interrupts::<DescNode>()
|
|
||||||
{
|
|
||||||
Some(Interruption::List)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(intr) = intr {
|
|
||||||
self.interrupt(intr, styles, false)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.accept(&styled.sub, styles)?;
|
self.accept(&styled.sub, styles)?;
|
||||||
|
self.interrupt_style(&styled.map, Some(styles))?;
|
||||||
if let Some(intr) = intr {
|
|
||||||
self.interrupt(intr, styles, true)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interrupt(
|
fn interrupt_style(
|
||||||
&mut self,
|
&mut self,
|
||||||
intr: Interruption,
|
map: &StyleMap,
|
||||||
styles: StyleChain<'a>,
|
styles: Option<StyleChain<'a>>,
|
||||||
keep: bool,
|
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
if intr >= Interruption::List && !self.list.items.is_empty() {
|
if map.interrupts::<PageNode>() {
|
||||||
|
self.interrupt_page(styles)?;
|
||||||
|
} else if map.interrupts::<ParNode>() {
|
||||||
|
self.interrupt_par()?;
|
||||||
|
} else if map.interrupts::<ListNode>()
|
||||||
|
|| map.interrupts::<EnumNode>()
|
||||||
|
|| map.interrupts::<DescNode>()
|
||||||
|
{
|
||||||
|
self.interrupt_list()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interrupt_list(&mut self) -> SourceResult<()> {
|
||||||
|
if !self.list.items.is_empty() {
|
||||||
let staged = mem::take(&mut self.list.staged);
|
let staged = mem::take(&mut self.list.staged);
|
||||||
let (list, styles) = mem::take(&mut self.list).finish();
|
let (list, styles) = mem::take(&mut self.list).finish();
|
||||||
let stored = self.scratch.content.alloc(list);
|
let stored = self.scratch.content.alloc(list);
|
||||||
@ -416,36 +401,30 @@ impl<'a> Builder<'a> {
|
|||||||
self.accept(content, styles)?;
|
self.accept(content, styles)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
if intr >= Interruption::Par && !self.par.0.is_empty() {
|
fn interrupt_par(&mut self) -> SourceResult<()> {
|
||||||
|
self.interrupt_list()?;
|
||||||
|
if !self.par.0.is_empty() {
|
||||||
let (par, styles) = mem::take(&mut self.par).finish();
|
let (par, styles) = mem::take(&mut self.par).finish();
|
||||||
let stored = self.scratch.content.alloc(par);
|
let stored = self.scratch.content.alloc(par);
|
||||||
self.accept(stored, styles)?;
|
self.accept(stored, styles)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(doc) = &mut self.doc {
|
|
||||||
if intr >= Interruption::Page
|
|
||||||
&& (!self.flow.0.is_empty() || (doc.keep_next && keep))
|
|
||||||
{
|
|
||||||
let (flow, shared) = mem::take(&mut self.flow).finish();
|
|
||||||
let styles =
|
|
||||||
if shared == StyleChain::default() { styles } else { shared };
|
|
||||||
let page = PageNode(flow).pack();
|
|
||||||
let stored = self.scratch.content.alloc(page);
|
|
||||||
self.accept(stored, styles)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sequence(
|
fn interrupt_page(&mut self, styles: Option<StyleChain<'a>>) -> SourceResult<()> {
|
||||||
&mut self,
|
self.interrupt_par()?;
|
||||||
seq: &'a SequenceNode,
|
let Some(doc) = &mut self.doc else { return Ok(()) };
|
||||||
styles: StyleChain<'a>,
|
if !self.flow.0.is_empty() || (doc.keep_next && styles.is_some()) {
|
||||||
) -> SourceResult<()> {
|
let (flow, shared) = mem::take(&mut self.flow).finish();
|
||||||
for content in &seq.0 {
|
let styles =
|
||||||
self.accept(content, styles)?;
|
if shared == StyleChain::default() { styles.unwrap() } else { shared };
|
||||||
|
let page = PageNode(flow).pack();
|
||||||
|
let stored = self.scratch.content.alloc(page);
|
||||||
|
self.accept(stored, styles)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user