summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/model/document.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-27 19:04:55 +0100
committerGitHub <noreply@github.com>2024-10-27 18:04:55 +0000
commitbe7cfc85d08c545abfac08098b7b33b4bd71f37e (patch)
treef4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-library/src/model/document.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-library/src/model/document.rs')
-rw-r--r--crates/typst-library/src/model/document.rs145
1 files changed, 145 insertions, 0 deletions
diff --git a/crates/typst-library/src/model/document.rs b/crates/typst-library/src/model/document.rs
new file mode 100644
index 00000000..b693d785
--- /dev/null
+++ b/crates/typst-library/src/model/document.rs
@@ -0,0 +1,145 @@
+use ecow::EcoString;
+
+use crate::diag::{bail, HintedStrResult, SourceResult};
+use crate::engine::Engine;
+use crate::foundations::{
+ cast, elem, Args, Array, Construct, Content, Datetime, Fields, Smart, StyleChain,
+ Styles, Value,
+};
+use crate::introspection::Introspector;
+use crate::layout::Page;
+
+/// The root element of a document and its metadata.
+///
+/// All documents are automatically wrapped in a `document` element. You cannot
+/// create a document element yourself. This function is only used with
+/// [set rules]($styling/#set-rules) to specify document metadata. Such a set
+/// rule must not occur inside of any layout container.
+///
+/// ```example
+/// #set document(title: [Hello])
+///
+/// This has no visible output, but
+/// embeds metadata into the PDF!
+/// ```
+///
+/// Note that metadata set with this function is not rendered within the
+/// document. Instead, it is embedded in the compiled PDF file.
+#[elem(Construct)]
+pub struct DocumentElem {
+ /// The document's title. This is often rendered as the title of the
+ /// PDF viewer window.
+ ///
+ /// While this can be arbitrary content, PDF viewers only support plain text
+ /// titles, so the conversion might be lossy.
+ #[ghost]
+ pub title: Option<Content>,
+
+ /// The document's authors.
+ #[ghost]
+ pub author: Author,
+
+ /// The document's keywords.
+ #[ghost]
+ pub keywords: Keywords,
+
+ /// The document's creation date.
+ ///
+ /// If this is `{auto}` (default), Typst uses the current date and time.
+ /// Setting it to `{none}` prevents Typst from embedding any creation date
+ /// into the PDF metadata.
+ ///
+ /// The year component must be at least zero in order to be embedded into a
+ /// PDF.
+ ///
+ /// If you want to create byte-by-byte reproducible PDFs, set this to
+ /// something other than `{auto}`.
+ #[ghost]
+ pub date: Smart<Option<Datetime>>,
+}
+
+impl Construct for DocumentElem {
+ fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
+ bail!(args.span, "can only be used in set rules")
+ }
+}
+
+/// A list of authors.
+#[derive(Debug, Default, Clone, PartialEq, Hash)]
+pub struct Author(Vec<EcoString>);
+
+cast! {
+ Author,
+ self => self.0.into_value(),
+ v: EcoString => Self(vec![v]),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
+}
+
+/// A list of keywords.
+#[derive(Debug, Default, Clone, PartialEq, Hash)]
+pub struct Keywords(Vec<EcoString>);
+
+cast! {
+ Keywords,
+ self => self.0.into_value(),
+ v: EcoString => Self(vec![v]),
+ v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
+}
+
+/// A finished document with metadata and page frames.
+#[derive(Debug, Default, Clone)]
+pub struct Document {
+ /// The document's finished pages.
+ pub pages: Vec<Page>,
+ /// Details about the document.
+ pub info: DocumentInfo,
+ /// Provides the ability to execute queries on the document.
+ pub introspector: Introspector,
+}
+
+/// Details about the document.
+#[derive(Debug, Default, Clone, PartialEq, Hash)]
+pub struct DocumentInfo {
+ /// The document's title.
+ pub title: Option<EcoString>,
+ /// The document's author.
+ pub author: Vec<EcoString>,
+ /// The document's keywords.
+ pub keywords: Vec<EcoString>,
+ /// The document's creation date.
+ pub date: Smart<Option<Datetime>>,
+}
+
+impl DocumentInfo {
+ /// Populate this document info with details from the given styles.
+ ///
+ /// Document set rules are a bit special, so we need to do this manually.
+ pub fn populate(&mut self, styles: &Styles) {
+ let chain = StyleChain::new(styles);
+ let has = |field| styles.has::<DocumentElem>(field as _);
+ if has(<DocumentElem as Fields>::Enum::Title) {
+ self.title =
+ DocumentElem::title_in(chain).map(|content| content.plain_text());
+ }
+ if has(<DocumentElem as Fields>::Enum::Author) {
+ self.author = DocumentElem::author_in(chain).0;
+ }
+ if has(<DocumentElem as Fields>::Enum::Keywords) {
+ self.keywords = DocumentElem::keywords_in(chain).0;
+ }
+ if has(<DocumentElem as Fields>::Enum::Date) {
+ self.date = DocumentElem::date_in(chain);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_document_is_send_and_sync() {
+ fn ensure_send_and_sync<T: Send + Sync>() {}
+ ensure_send_and_sync::<Document>();
+ }
+}