summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-26 23:42:40 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-26 23:52:01 +0100
commit6bafc6391061d4b589dea835705a08b25a4df9f8 (patch)
tree4add85f17fc56da341acfb58a223ea20d80c280a
parent0579fd4409375aaa9fd8e87a06fd59097b5fcd97 (diff)
Document metadata
-rw-r--r--cli/src/main.rs4
-rw-r--r--library/src/layout/mod.rs37
-rw-r--r--library/src/lib.rs1
-rw-r--r--library/src/prelude.rs2
-rw-r--r--library/src/structure/doc.rs32
-rw-r--r--library/src/structure/document.rs47
-rw-r--r--library/src/structure/mod.rs4
-rw-r--r--src/doc.rs (renamed from src/frame.rs)20
-rw-r--r--src/export/pdf/mod.rs30
-rw-r--r--src/export/pdf/page.rs8
-rw-r--r--src/export/render.rs2
-rw-r--r--src/lib.rs22
-rw-r--r--src/model/cast.rs2
-rw-r--r--src/model/eval.rs6
-rw-r--r--src/model/library.rs4
-rw-r--r--src/model/styles.rs41
-rw-r--r--src/model/typeset.rs10
-rw-r--r--tests/ref/layout/page-box.pngbin756 -> 0 bytes
-rw-r--r--tests/src/benches.rs4
-rw-r--r--tests/src/tests.rs13
-rw-r--r--tests/typ/layout/page-box.typ14
-rw-r--r--tests/typ/style/document.typ30
22 files changed, 204 insertions, 129 deletions
diff --git a/cli/src/main.rs b/cli/src/main.rs
index 2825f74c..5463c3ec 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -236,8 +236,8 @@ fn compile_once(world: &mut SystemWorld, command: &CompileCommand) -> StrResult<
match typst::compile(world, source) {
// Export the PDF.
- Ok(frames) => {
- let buffer = typst::export::pdf(&frames);
+ Ok(document) => {
+ let buffer = typst::export::pdf(&document);
fs::write(&command.output, buffer).map_err(|_| "failed to write PDF file")?;
status(command, Status::Success).unwrap();
}
diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs
index 100e0611..e63a072e 100644
--- a/library/src/layout/mod.rs
+++ b/library/src/layout/mod.rs
@@ -29,7 +29,7 @@ use std::mem;
use comemo::Tracked;
use typed_arena::Arena;
use typst::diag::SourceResult;
-use typst::frame::Frame;
+use typst::doc::Frame;
use typst::geom::*;
use typst::model::{
applicable, capability, realize, Content, Node, SequenceNode, Style, StyleChain,
@@ -40,7 +40,7 @@ use typst::World;
use crate::prelude::*;
use crate::shared::BehavedBuilder;
use crate::structure::{
- DescNode, DocNode, EnumNode, ListItem, ListNode, DESC, ENUM, LIST,
+ DescNode, DocumentNode, EnumNode, ListItem, ListNode, DESC, ENUM, LIST,
};
use crate::text::{
LinebreakNode, ParNode, ParbreakNode, SmartQuoteNode, SpaceNode, TextNode,
@@ -54,7 +54,7 @@ pub trait LayoutRoot {
&self,
world: Tracked<dyn World>,
styles: StyleChain,
- ) -> SourceResult<Vec<Frame>>;
+ ) -> SourceResult<Document>;
}
impl LayoutRoot for Content {
@@ -63,7 +63,7 @@ impl LayoutRoot for Content {
&self,
world: Tracked<dyn World>,
styles: StyleChain,
- ) -> SourceResult<Vec<Frame>> {
+ ) -> SourceResult<Document> {
let scratch = Scratch::default();
let (realized, styles) = realize_root(world, &scratch, self, styles)?;
realized.with::<dyn LayoutRoot>().unwrap().layout_root(world, styles)
@@ -245,7 +245,7 @@ fn realize_root<'a>(
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles))?;
let (pages, shared) = builder.doc.unwrap().pages.finish();
- Ok((DocNode(pages).pack(), shared))
+ Ok((DocumentNode(pages).pack(), shared))
}
/// Realize into a node that is capable of block-level layout.
@@ -357,6 +357,10 @@ impl<'a> Builder<'a> {
}
}
+ if let Some(span) = content.span() {
+ bail!(span, "not allowed here");
+ }
+
Ok(())
}
@@ -378,13 +382,26 @@ impl<'a> Builder<'a> {
map: &StyleMap,
styles: Option<StyleChain<'a>>,
) -> SourceResult<()> {
- if map.interrupts::<PageNode>() {
+ if let Some(Some(span)) = map.interruption::<DocumentNode>() {
+ if self.doc.is_none() {
+ bail!(span, "not allowed here");
+ }
+ if !self.flow.0.is_empty()
+ || !self.par.0.is_empty()
+ || !self.list.items.is_empty()
+ {
+ bail!(span, "must appear before any content");
+ }
+ } else if let Some(Some(span)) = map.interruption::<PageNode>() {
+ if self.doc.is_none() {
+ bail!(span, "not allowed here");
+ }
self.interrupt_page(styles)?;
- } else if map.interrupts::<ParNode>() {
+ } else if map.interruption::<ParNode>().is_some() {
self.interrupt_par()?;
- } else if map.interrupts::<ListNode>()
- || map.interrupts::<EnumNode>()
- || map.interrupts::<DescNode>()
+ } else if map.interruption::<ListNode>().is_some()
+ || map.interruption::<EnumNode>().is_some()
+ || map.interruption::<DescNode>().is_some()
{
self.interrupt_list()?;
}
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 6107cf42..cf8cb490 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -44,6 +44,7 @@ fn scope() -> Scope {
std.def_fn("smallcaps", text::smallcaps);
// Structure.
+ std.def_node::<structure::DocumentNode>("document");
std.def_node::<structure::RefNode>("ref");
std.def_node::<structure::HeadingNode>("heading");
std.def_node::<structure::ListNode>("list");
diff --git a/library/src/prelude.rs b/library/src/prelude.rs
index 6379e579..bc0ec31d 100644
--- a/library/src/prelude.rs
+++ b/library/src/prelude.rs
@@ -10,7 +10,7 @@ pub use comemo::Tracked;
#[doc(no_inline)]
pub use typst::diag::{bail, error, with_alternative, At, SourceResult, StrResult};
#[doc(no_inline)]
-pub use typst::frame::*;
+pub use typst::doc::*;
#[doc(no_inline)]
pub use typst::geom::*;
#[doc(no_inline)]
diff --git a/library/src/structure/doc.rs b/library/src/structure/doc.rs
deleted file mode 100644
index e471a852..00000000
--- a/library/src/structure/doc.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use crate::layout::{LayoutRoot, PageNode};
-use crate::prelude::*;
-
-/// A sequence of page runs.
-#[derive(Hash)]
-pub struct DocNode(pub StyleVec<PageNode>);
-
-#[node(LayoutRoot)]
-impl DocNode {}
-
-impl LayoutRoot for DocNode {
- /// Layout the document into a sequence of frames, one per page.
- fn layout_root(
- &self,
- world: Tracked<dyn World>,
- styles: StyleChain,
- ) -> SourceResult<Vec<Frame>> {
- let mut frames = vec![];
- for (page, map) in self.0.iter() {
- let number = 1 + frames.len();
- frames.extend(page.layout(world, number, styles.chain(map))?);
- }
- Ok(frames)
- }
-}
-
-impl Debug for DocNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("Doc ")?;
- self.0.fmt(f)
- }
-}
diff --git a/library/src/structure/document.rs b/library/src/structure/document.rs
new file mode 100644
index 00000000..2e5761e0
--- /dev/null
+++ b/library/src/structure/document.rs
@@ -0,0 +1,47 @@
+use crate::layout::{LayoutRoot, PageNode};
+use crate::prelude::*;
+
+/// The root node of the model.
+#[derive(Hash)]
+pub struct DocumentNode(pub StyleVec<PageNode>);
+
+#[node(LayoutRoot)]
+impl DocumentNode {
+ /// The document's title.
+ #[property(referenced)]
+ pub const TITLE: Option<EcoString> = None;
+
+ /// The document's author.
+ #[property(referenced)]
+ pub const AUTHOR: Option<EcoString> = None;
+}
+
+impl LayoutRoot for DocumentNode {
+ /// Layout the document into a sequence of frames, one per page.
+ fn layout_root(
+ &self,
+ world: Tracked<dyn World>,
+ styles: StyleChain,
+ ) -> SourceResult<Document> {
+ let mut pages = vec![];
+ for (page, map) in self.0.iter() {
+ let number = 1 + pages.len();
+ pages.extend(page.layout(world, number, styles.chain(map))?);
+ }
+
+ Ok(Document {
+ metadata: Metadata {
+ title: styles.get(Self::TITLE).clone(),
+ author: styles.get(Self::AUTHOR).clone(),
+ },
+ pages,
+ })
+ }
+}
+
+impl Debug for DocumentNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.write_str("Document ")?;
+ self.0.fmt(f)
+ }
+}
diff --git a/library/src/structure/mod.rs b/library/src/structure/mod.rs
index 8e13f76a..a1c27eed 100644
--- a/library/src/structure/mod.rs
+++ b/library/src/structure/mod.rs
@@ -1,12 +1,12 @@
//! Document structuring.
-mod doc;
+mod document;
mod heading;
mod list;
mod reference;
mod table;
-pub use self::doc::*;
+pub use self::document::*;
pub use self::heading::*;
pub use self::list::*;
pub use self::reference::*;
diff --git a/src/frame.rs b/src/doc.rs
index f7d05a1d..f65d5ae6 100644
--- a/src/frame.rs
+++ b/src/doc.rs
@@ -1,4 +1,4 @@
-//! Finished layouts.
+//! Finished documents.
use std::fmt::{self, Debug, Formatter, Write};
use std::num::NonZeroUsize;
@@ -13,6 +13,24 @@ use crate::image::Image;
use crate::model::{dict, Dict, Value};
use crate::util::EcoString;
+/// A finished document with metadata and page frames.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Document {
+ /// The document's metadata.
+ pub metadata: Metadata,
+ /// The page frames.
+ pub pages: Vec<Frame>,
+}
+
+/// Document metadata.
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct Metadata {
+ /// The document's title.
+ pub title: Option<EcoString>,
+ /// The document's author.
+ pub author: Option<EcoString>,
+}
+
/// A finished layout with elements at fixed positions.
#[derive(Default, Clone, Eq, PartialEq)]
pub struct Frame {
diff --git a/src/export/pdf/mod.rs b/src/export/pdf/mod.rs
index 2547ddbf..7a530f04 100644
--- a/src/export/pdf/mod.rs
+++ b/src/export/pdf/mod.rs
@@ -14,21 +14,21 @@ use pdf_writer::{Finish, Name, PdfWriter, Ref, TextStr};
use self::outline::{Heading, HeadingNode};
use self::page::Page;
+use crate::doc::{Document, Lang, Metadata};
use crate::font::Font;
-use crate::frame::{Frame, Lang};
use crate::geom::{Abs, Dir, Em};
use crate::image::Image;
-/// Export a collection of frames into a PDF file.
+/// Export a document into a PDF file.
///
/// This creates one page per frame. In addition to the frames, you need to pass
/// in the context used during compilation so that fonts and images can be
/// included in the PDF.
///
/// Returns the raw bytes making up the PDF file.
-pub fn pdf(frames: &[Frame]) -> Vec<u8> {
- let mut ctx = PdfContext::new();
- page::construct_pages(&mut ctx, frames);
+pub fn pdf(document: &Document) -> Vec<u8> {
+ let mut ctx = PdfContext::new(&document.metadata);
+ page::construct_pages(&mut ctx, &document.pages);
font::write_fonts(&mut ctx);
image::write_images(&mut ctx);
page::write_page_tree(&mut ctx);
@@ -41,7 +41,8 @@ const SRGB: Name<'static> = Name(b"srgb");
const D65_GRAY: Name<'static> = Name(b"d65gray");
/// Context for exporting a whole PDF document.
-pub struct PdfContext {
+pub struct PdfContext<'a> {
+ metadata: &'a Metadata,
writer: PdfWriter,
pages: Vec<Page>,
page_heights: Vec<f32>,
@@ -57,11 +58,12 @@ pub struct PdfContext {
heading_tree: Vec<HeadingNode>,
}
-impl PdfContext {
- fn new() -> Self {
+impl<'a> PdfContext<'a> {
+ fn new(metadata: &'a Metadata) -> Self {
let mut alloc = Ref::new(1);
let page_tree_ref = alloc.bump();
Self {
+ metadata,
writer: PdfWriter::new(),
pages: vec![],
page_heights: vec![],
@@ -117,7 +119,15 @@ fn write_catalog(ctx: &mut PdfContext) {
};
// Write the document information.
- ctx.writer.document_info(ctx.alloc.bump()).creator(TextStr("Typst"));
+ let mut info = ctx.writer.document_info(ctx.alloc.bump());
+ if let Some(title) = &ctx.metadata.title {
+ info.title(TextStr(title));
+ }
+ if let Some(author) = &ctx.metadata.author {
+ info.author(TextStr(author));
+ }
+ info.creator(TextStr("Typst"));
+ info.finish();
// Write the document catalog.
let mut catalog = ctx.writer.catalog(ctx.alloc.bump());
@@ -131,8 +141,6 @@ fn write_catalog(ctx: &mut PdfContext) {
if let Some(lang) = lang {
catalog.lang(TextStr(lang.as_str()));
}
-
- catalog.finish();
}
/// Compress data with the DEFLATE algorithm.
diff --git a/src/export/pdf/page.rs b/src/export/pdf/page.rs
index 3167989c..7c479425 100644
--- a/src/export/pdf/page.rs
+++ b/src/export/pdf/page.rs
@@ -5,8 +5,8 @@ use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str};
use super::{
deflate, AbsExt, EmExt, Heading, HeadingNode, PdfContext, RefExt, D65_GRAY, SRGB,
};
+use crate::doc::{Destination, Element, Frame, Group, Role, Text};
use crate::font::Font;
-use crate::frame::{Destination, Element, Frame, Group, Role, Text};
use crate::geom::{
self, Abs, Color, Em, Geometry, Numeric, Paint, Point, Ratio, Shape, Size, Stroke,
Transform,
@@ -155,8 +155,8 @@ pub struct Page {
}
/// An exporter for the contents of a single PDF page.
-struct PageContext<'a> {
- parent: &'a mut PdfContext,
+struct PageContext<'a, 'b> {
+ parent: &'a mut PdfContext<'b>,
page_ref: Ref,
content: Content,
state: State,
@@ -177,7 +177,7 @@ struct State {
stroke_space: Option<Name<'static>>,
}
-impl<'a> PageContext<'a> {
+impl PageContext<'_, '_> {
fn save_state(&mut self) {
self.saves.push(self.state.clone());
self.content.save_state();
diff --git a/src/export/render.rs b/src/export/render.rs
index 7cff7ad8..14654b9b 100644
--- a/src/export/render.rs
+++ b/src/export/render.rs
@@ -8,7 +8,7 @@ use tiny_skia as sk;
use ttf_parser::{GlyphId, OutlineBuilder};
use usvg::FitTo;
-use crate::frame::{Element, Frame, Group, Text};
+use crate::doc::{Element, Frame, Group, Text};
use crate::geom::{
self, Abs, Geometry, Paint, PathElement, Shape, Size, Stroke, Transform,
};
diff --git a/src/lib.rs b/src/lib.rs
index 6e0a68a5..7b903d4e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,9 +11,8 @@
//! in the source file. The nodes of the content tree are well structured and
//! order-independent and thus much better suited for further processing than
//! the raw markup.
-//! - **Typesetting:** Next, the content is [typeset] into a collection of
-//! [`Frame`]s (one per page) with elements and fixed positions, ready for
-//! exporting.
+//! - **Typesetting:** Next, the content is [typeset] into a [document]
+//! containing one [frame] per page with elements and fixed positions.
//! - **Exporting:** These frames can finally be exported into an output format
//! (currently supported are [PDF] and [raster images]).
//!
@@ -25,6 +24,8 @@
//! [module]: model::Module
//! [content]: model::Content
//! [typeset]: model::typeset
+//! [document]: doc::Document
+//! [frame]: doc::Frame
//! [PDF]: export::pdf
//! [raster images]: export::render
@@ -38,9 +39,9 @@ pub mod geom;
pub mod diag;
#[macro_use]
pub mod model;
+pub mod doc;
pub mod export;
pub mod font;
-pub mod frame;
pub mod image;
pub mod syntax;
@@ -49,21 +50,14 @@ use std::path::Path;
use comemo::{Prehashed, Track};
use crate::diag::{FileResult, SourceResult};
+use crate::doc::Document;
use crate::font::{Font, FontBook};
-use crate::frame::Frame;
use crate::model::{Library, Route};
use crate::syntax::{Source, SourceId};
use crate::util::Buffer;
-/// Compile a source file into a collection of layouted frames.
-///
-/// Returns either a vector of frames representing individual pages or
-/// diagnostics in the form of a vector of error message with file and span
-/// information.
-pub fn compile(
- world: &(dyn World + 'static),
- source: &Source,
-) -> SourceResult<Vec<Frame>> {
+/// Compile a source file into a fully layouted document.
+pub fn compile(world: &(dyn World + 'static), source: &Source) -> SourceResult<Document> {
// Evaluate the source file into a module.
let route = Route::default();
let module = model::eval(world.track(), route.track(), source)?;
diff --git a/src/model/cast.rs b/src/model/cast.rs
index d0a4650a..a4a3fe4e 100644
--- a/src/model/cast.rs
+++ b/src/model/cast.rs
@@ -3,8 +3,8 @@ use std::str::FromStr;
use super::{Content, Regex, Selector, Transform, Value};
use crate::diag::{with_alternative, StrResult};
+use crate::doc::{Destination, Lang, Location, Region};
use crate::font::{FontStretch, FontStyle, FontWeight};
-use crate::frame::{Destination, Lang, Location, Region};
use crate::geom::{
Axes, Corners, Dir, GenAlign, Get, Length, Paint, PartialStroke, Point, Rel, Sides,
};
diff --git a/src/model/eval.rs b/src/model/eval.rs
index da7036b7..166dadde 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -21,10 +21,6 @@ use crate::util::{format_eco, EcoString, PathExt};
use crate::World;
/// Evaluate a source file and return the resulting module.
-///
-/// Returns either a module containing a scope with top-level bindings and
-/// layoutable contents or diagnostics in the form of a vector of error
-/// messages with file and span information.
#[comemo::memoize]
pub fn eval(
world: Tracked<dyn World>,
@@ -934,7 +930,7 @@ impl Eval for ast::SetRule {
let target = self.target();
let target = target.eval(vm)?.cast::<Func>().at(target.span())?;
let args = self.args().eval(vm)?;
- target.set(args)
+ Ok(target.set(args)?.spanned(self.span()))
}
}
diff --git a/src/model/library.rs b/src/model/library.rs
index 2ee09b27..518caca1 100644
--- a/src/model/library.rs
+++ b/src/model/library.rs
@@ -7,7 +7,7 @@ use once_cell::sync::OnceCell;
use super::{Content, NodeId, Scope, StyleChain, StyleMap};
use crate::diag::SourceResult;
-use crate::frame::Frame;
+use crate::doc::Document;
use crate::geom::{Abs, Dir};
use crate::util::{hash128, EcoString};
use crate::World;
@@ -31,7 +31,7 @@ pub struct LangItems {
world: Tracked<dyn World>,
content: &Content,
styles: StyleChain,
- ) -> SourceResult<Vec<Frame>>,
+ ) -> SourceResult<Document>,
/// Access the em size.
pub em: fn(StyleChain) -> Abs,
/// Access the text direction.
diff --git a/src/model/styles.rs b/src/model/styles.rs
index f3cfb648..80ec0d1e 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -79,9 +79,25 @@ impl StyleMap {
self
}
- /// Whether this map contains styles for the given `node.`
- pub fn interrupts<T: 'static>(&self) -> bool {
- self.0.iter().any(|entry| entry.is_of(NodeId::of::<T>()))
+ /// Add an origin span to all contained properties.
+ pub fn spanned(mut self, span: Span) -> Self {
+ for entry in &mut self.0 {
+ if let Style::Property(property) = entry {
+ property.origin = Some(span);
+ }
+ }
+ self
+ }
+
+ /// Returns `Some(_)` with an optional span if this map contains styles for
+ /// the given `node`.
+ pub fn interruption<T: 'static>(&self) -> Option<Option<Span>> {
+ let node = NodeId::of::<T>();
+ self.0.iter().find_map(|entry| match entry {
+ Style::Property(property) => property.is_of(node).then(|| property.origin),
+ Style::Recipe(recipe) => recipe.is_of(node).then(|| Some(recipe.span)),
+ _ => None,
+ })
}
}
@@ -127,15 +143,6 @@ impl Style {
_ => None,
}
}
-
- /// Whether this entry contains styles for the given `node.`
- pub fn is_of(&self, node: NodeId) -> bool {
- match self {
- Self::Property(property) => property.is_of(node),
- Self::Recipe(recipe) => recipe.is_of(node),
- _ => false,
- }
- }
}
impl Debug for Style {
@@ -162,6 +169,8 @@ pub struct Property {
scoped: bool,
/// The property's value.
value: Arc<Prehashed<dyn Bounds>>,
+ /// The span of the set rule the property stems from.
+ origin: Option<Span>,
/// The name of the property.
#[cfg(debug_assertions)]
name: &'static str,
@@ -175,6 +184,7 @@ impl Property {
node: K::node(),
value: Arc::new(Prehashed::new(value)),
scoped: false,
+ origin: None,
#[cfg(debug_assertions)]
name: K::NAME,
}
@@ -330,8 +340,11 @@ impl Recipe {
let args = Args::new(self.span, [Value::Content(content.clone())]);
let mut result = func.call_detached(world, args);
if let Some(span) = content.span() {
- let point = || Tracepoint::Show(content.name().into());
- result = result.trace(world, point, span);
+ // For selector-less show rules, a tracepoint makes no sense.
+ if self.selector.is_some() {
+ let point = || Tracepoint::Show(content.name().into());
+ result = result.trace(world, point, span);
+ }
}
Ok(result?.display())
}
diff --git a/src/model/typeset.rs b/src/model/typeset.rs
index ad2af3b2..451c6eb0 100644
--- a/src/model/typeset.rs
+++ b/src/model/typeset.rs
@@ -2,16 +2,12 @@ use comemo::Tracked;
use super::{Content, StyleChain};
use crate::diag::SourceResult;
-use crate::frame::Frame;
+use crate::doc::Document;
use crate::World;
-/// Typeset content into a collection of layouted frames.
-///
-/// Returns either a vector of frames representing individual pages or
-/// diagnostics in the form of a vector of error message with file and span
-/// information.
+/// Typeset content into a fully layouted document.
#[comemo::memoize]
-pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Vec<Frame>> {
+pub fn typeset(world: Tracked<dyn World>, content: &Content) -> SourceResult<Document> {
let library = world.library();
let styles = StyleChain::new(&library.styles);
(library.items.layout)(world, content, styles)
diff --git a/tests/ref/layout/page-box.png b/tests/ref/layout/page-box.png
deleted file mode 100644
index c8d0320c..00000000
--- a/tests/ref/layout/page-box.png
+++ /dev/null
Binary files differ
diff --git a/tests/src/benches.rs b/tests/src/benches.rs
index 754f80a9..1ae012eb 100644
--- a/tests/src/benches.rs
+++ b/tests/src/benches.rs
@@ -94,8 +94,8 @@ fn bench_compile(iai: &mut Iai) {
fn bench_render(iai: &mut Iai) {
let world = BenchWorld::new();
- let frames = typst::compile(&world, &world.source).unwrap();
- iai.run(|| typst::export::render(&frames[0], 1.0))
+ let document = typst::compile(&world, &world.source).unwrap();
+ iai.run(|| typst::export::render(&document.pages[0], 1.0))
}
struct BenchWorld {
diff --git a/tests/src/tests.rs b/tests/src/tests.rs
index f96c651b..215d7fe7 100644
--- a/tests/src/tests.rs
+++ b/tests/src/tests.rs
@@ -12,8 +12,8 @@ use elsa::FrozenVec;
use once_cell::unsync::OnceCell;
use tiny_skia as sk;
use typst::diag::{bail, FileError, FileResult};
+use typst::doc::{Document, Element, Frame, Metadata};
use typst::font::{Font, FontBook};
-use typst::frame::{Element, Frame};
use typst::geom::{Abs, RgbaColor, Sides};
use typst::model::{Library, Smart, Value};
use typst::syntax::{Source, SourceId, SyntaxNode};
@@ -349,20 +349,21 @@ fn test(
line += part.lines().count() + 1;
}
+ let document = Document { pages: frames, metadata: Metadata::default() };
if compare_ever {
if let Some(pdf_path) = pdf_path {
- let pdf_data = typst::export::pdf(&frames);
+ let pdf_data = typst::export::pdf(&document);
fs::create_dir_all(&pdf_path.parent().unwrap()).unwrap();
fs::write(pdf_path, pdf_data).unwrap();
}
if world.print.frames {
- for frame in &frames {
+ for frame in &document.pages {
println!("Frame:\n{:#?}\n", frame);
}
}
- let canvas = render(&frames);
+ let canvas = render(&document.pages);
fs::create_dir_all(&png_path.parent().unwrap()).unwrap();
canvas.save_png(png_path).unwrap();
@@ -378,7 +379,7 @@ fn test(
println!(" Does not match reference image. ❌");
ok = false;
}
- } else if !frames.is_empty() {
+ } else if !document.pages.is_empty() {
println!(" Failed to open reference image. ❌");
ok = false;
}
@@ -425,7 +426,7 @@ fn test_part(
}
let (mut frames, errors) = match typst::compile(world, source) {
- Ok(frames) => (frames, vec![]),
+ Ok(document) => (document.pages, vec![]),
Err(errors) => (vec![], *errors),
};
diff --git a/tests/typ/layout/page-box.typ b/tests/typ/layout/page-box.typ
deleted file mode 100644
index ed9d3e14..00000000
--- a/tests/typ/layout/page-box.typ
+++ /dev/null
@@ -1,14 +0,0 @@
-// Test that you can't do page related stuff in a container.
-
----
-A
-#box[
- B
- #pagebreak()
- #set page("a4")
-]
-C
-
-// No consequences from the page("A4") call here.
-#pagebreak()
-D
diff --git a/tests/typ/style/document.typ b/tests/typ/style/document.typ
new file mode 100644
index 00000000..1fcb8109
--- /dev/null
+++ b/tests/typ/style/document.typ
@@ -0,0 +1,30 @@
+// Test document and page-level styles.
+
+---
+// This is okay.
+// Ref: false
+#set document(title: "Hello")
+
+---
+Hello
+
+// Error: 1-30 must appear before any content
+#set document(title: "Hello")
+
+---
+#box[
+ // Error: 3-32 not allowed here
+ #set document(title: "Hello")
+]
+
+---
+#box[
+ // Error: 3-18 not allowed here
+ #set page("a4")
+]
+
+---
+#box[
+ // Error: 3-15 not allowed here
+ #pagebreak()
+]