diff --git a/Cargo.lock b/Cargo.lock index 1ed105d8e..b8432b391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3471,9 +3471,9 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "xmp-writer" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd742bbbb930fc972b28bf66b7546dfbc7bb9a4c7924299df0ae6a5641fcadf" +checksum = "4543ba138f64a94b19e1e9c66c165bca7e03d470e1c066cb76ea279d9d0e1989" [[package]] name = "xz2" diff --git a/crates/typst-library/src/meta/document.rs b/crates/typst-library/src/meta/document.rs index f81d8f8a2..57ce064c1 100644 --- a/crates/typst-library/src/meta/document.rs +++ b/crates/typst-library/src/meta/document.rs @@ -1,3 +1,5 @@ +use typst::eval::Datetime; + use crate::layout::{LayoutRoot, PageElem}; use crate::meta::ManualPageCounter; use crate::prelude::*; @@ -30,6 +32,12 @@ pub struct DocumentElem { /// The document's keywords. pub keywords: Keywords, + /// The document's creation date. + /// + /// The year component must be at least zero in order to be embedded into + /// a PDF. + pub date: Option, + /// The page runs. #[internal] #[variadic] @@ -81,6 +89,7 @@ impl LayoutRoot for DocumentElem { title: self.title(styles), author: self.author(styles).0, keywords: self.keywords(styles).0, + date: self.date(styles), }) } } diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml index 2db1d7020..7df455d7e 100644 --- a/crates/typst/Cargo.toml +++ b/crates/typst/Cargo.toml @@ -53,7 +53,7 @@ unicode-segmentation = "1" unscanny = "0.1" usvg = { version = "0.35", default-features = false, features = ["text"] } xmlwriter = "0.1.0" -xmp-writer = "0.1" +xmp-writer = "0.2" time = { version = "0.3.20", features = ["std", "formatting", "macros", "parsing"] } wasmi = "0.31.0" xmlparser = "0.13.5" diff --git a/crates/typst/src/doc.rs b/crates/typst/src/doc.rs index 6fddc2810..890a94a53 100644 --- a/crates/typst/src/doc.rs +++ b/crates/typst/src/doc.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use ecow::{eco_format, EcoString}; -use crate::eval::{cast, dict, ty, Dict, Repr, Value}; +use crate::eval::{cast, dict, ty, Datetime, Dict, Repr, Value}; use crate::export::PdfPageLabel; use crate::font::Font; use crate::geom::{ @@ -30,6 +30,8 @@ pub struct Document { pub author: Vec, /// The document's keywords. pub keywords: Vec, + /// The document's creation date. + pub date: Option, } /// A finished layout with items at fixed positions. diff --git a/crates/typst/src/export/pdf/mod.rs b/crates/typst/src/export/pdf/mod.rs index 51784fbcd..c64b95b0c 100644 --- a/crates/typst/src/export/pdf/mod.rs +++ b/crates/typst/src/export/pdf/mod.rs @@ -171,6 +171,24 @@ fn write_catalog(ctx: &mut PdfContext) { xmp.pdf_keywords(&joined); } + if let Some(date) = ctx.document.date { + if let Some(year) = date.year().filter(|&y| y >= 0) { + let mut pdf_date = pdf_writer::Date::new(year as u16); + if let Some(month) = date.month() { + pdf_date = pdf_date.month(month); + } + if let Some(day) = date.day() { + pdf_date = pdf_date.day(day); + } + info.creation_date(pdf_date); + + let mut xmp_date = xmp_writer::DateTime::year(year as u16); + xmp_date.month = date.month(); + xmp_date.day = date.day(); + xmp.create_date(xmp_date); + } + } + info.finish(); xmp.num_pages(ctx.document.pages.len() as u32); xmp.format("application/pdf"); diff --git a/tests/typ/meta/document.typ b/tests/typ/meta/document.typ index f4676f3cc..814d547d0 100644 --- a/tests/typ/meta/document.typ +++ b/tests/typ/meta/document.typ @@ -8,7 +8,11 @@ What's up? --- // This, too. // Ref: false -#set document(author: ("A", "B")) +#set document(author: ("A", "B"), date: datetime.today()) + +--- +// Error: 21-28 expected datetime or none, found string +#set document(date: "today") --- // This, too.