mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 09:52:27 +00:00
feat: support creation-timestamp configuration for exporting PDF (#439)
* feat: support creation-timestamp configuration for exporting PDF * fix: respect config
This commit is contained in:
parent
25c449c2b2
commit
103e0f3b3e
11 changed files with 97 additions and 16 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -439,8 +439,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
|||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
|
@ -3740,6 +3742,7 @@ dependencies = [
|
|||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"cargo_metadata",
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap_builder",
|
||||
"clap_complete",
|
||||
|
@ -3807,6 +3810,7 @@ version = "0.11.15"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"biblatex",
|
||||
"chrono",
|
||||
"comemo 0.4.0",
|
||||
"dashmap",
|
||||
"ecow 0.2.2",
|
||||
|
|
|
@ -40,6 +40,7 @@ tokio-util = { version = "0.7.10", features = ["compat"] }
|
|||
open = { version = "5.1.3" }
|
||||
parking_lot = "0.12.1"
|
||||
walkdir = "2"
|
||||
chrono = "0.4"
|
||||
|
||||
# Networking
|
||||
hyper = { version = "0.14", features = ["full"] }
|
||||
|
|
|
@ -31,6 +31,7 @@ walkdir.workspace = true
|
|||
indexmap.workspace = true
|
||||
ecow.workspace = true
|
||||
siphasher.workspace = true
|
||||
chrono.workspace = true
|
||||
|
||||
typst.workspace = true
|
||||
|
||||
|
|
|
@ -143,10 +143,11 @@ mod polymorphic {
|
|||
Merged,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExportKind {
|
||||
#[default]
|
||||
Pdf,
|
||||
Pdf {
|
||||
creation_timestamp: Option<chrono::DateTime<chrono::Utc>>,
|
||||
},
|
||||
Svg {
|
||||
page: PageSelection,
|
||||
},
|
||||
|
@ -155,10 +156,18 @@ mod polymorphic {
|
|||
},
|
||||
}
|
||||
|
||||
impl Default for ExportKind {
|
||||
fn default() -> Self {
|
||||
Self::Pdf {
|
||||
creation_timestamp: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExportKind {
|
||||
pub fn extension(&self) -> &str {
|
||||
match self {
|
||||
Self::Pdf => "pdf",
|
||||
Self::Pdf { .. } => "pdf",
|
||||
Self::Svg { .. } => "svg",
|
||||
Self::Png { .. } => "png",
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ tinymist-assets = { workspace = true }
|
|||
tinymist-query.workspace = true
|
||||
tinymist-render.workspace = true
|
||||
sync-lsp.workspace = true
|
||||
chrono.workspace = true
|
||||
|
||||
once_cell.workspace = true
|
||||
anyhow.workspace = true
|
||||
|
|
|
@ -59,7 +59,9 @@ impl LanguageState {
|
|||
output: self.compile_config().output_path.clone(),
|
||||
mode: self.compile_config().export_pdf,
|
||||
},
|
||||
kind: ExportKind::Pdf,
|
||||
kind: ExportKind::Pdf {
|
||||
creation_timestamp: self.config.compile.determine_creation_timestamp(),
|
||||
},
|
||||
count_words: self.config.compile.notify_status,
|
||||
});
|
||||
|
||||
|
|
|
@ -27,7 +27,13 @@ struct ExportOpts {
|
|||
impl LanguageState {
|
||||
/// Export the current document as PDF file(s).
|
||||
pub fn export_pdf(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult {
|
||||
self.export(req_id, ExportKind::Pdf, args)
|
||||
self.export(
|
||||
req_id,
|
||||
ExportKind::Pdf {
|
||||
creation_timestamp: self.config.compile.determine_creation_timestamp(),
|
||||
},
|
||||
args,
|
||||
)
|
||||
}
|
||||
|
||||
/// Export the current document as Svg file(s).
|
||||
|
|
|
@ -500,7 +500,8 @@ impl CompileConfig {
|
|||
entry: command.input.map(|e| Path::new(&e).into()),
|
||||
root_dir: command.root,
|
||||
inputs: Arc::new(Prehashed::new(inputs)),
|
||||
font_paths: command.font.font_paths,
|
||||
font: command.font,
|
||||
creation_timestamp: command.creation_timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -621,13 +622,17 @@ impl CompileConfig {
|
|||
let font = || {
|
||||
let mut opts = self.font_opts.clone();
|
||||
|
||||
if let Some(system_fonts) = self.system_fonts {
|
||||
if let Some(system_fonts) = self.system_fonts.or_else(|| {
|
||||
self.typst_extra_args
|
||||
.as_ref()
|
||||
.map(|x| x.font.ignore_system_fonts)
|
||||
}) {
|
||||
opts.ignore_system_fonts = !system_fonts;
|
||||
}
|
||||
|
||||
let font_paths = (!self.font_paths.is_empty()).then_some(&self.font_paths);
|
||||
let font_paths =
|
||||
font_paths.or_else(|| self.typst_extra_args.as_ref().map(|x| &x.font_paths));
|
||||
font_paths.or_else(|| self.typst_extra_args.as_ref().map(|x| &x.font.font_paths));
|
||||
if let Some(paths) = font_paths {
|
||||
opts.font_paths.clone_from(paths);
|
||||
}
|
||||
|
@ -669,6 +674,11 @@ impl CompileConfig {
|
|||
combine(user_inputs, self.lsp_inputs.clone())
|
||||
}
|
||||
|
||||
/// Determines the creation timestamp.
|
||||
pub fn determine_creation_timestamp(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||
self.typst_extra_args.as_ref()?.creation_timestamp
|
||||
}
|
||||
|
||||
fn determine_user_inputs(&self) -> ImmutDict {
|
||||
static EMPTY: Lazy<ImmutDict> = Lazy::new(ImmutDict::default);
|
||||
|
||||
|
@ -686,13 +696,13 @@ impl CompileConfig {
|
|||
) -> (
|
||||
Option<bool>,
|
||||
&Vec<PathBuf>,
|
||||
Option<&Vec<PathBuf>>,
|
||||
Option<&CompileFontArgs>,
|
||||
Option<Arc<Path>>,
|
||||
) {
|
||||
(
|
||||
self.system_fonts,
|
||||
&self.font_paths,
|
||||
self.typst_extra_args.as_ref().map(|e| &e.font_paths),
|
||||
self.typst_extra_args.as_ref().map(|e| &e.font),
|
||||
self.determine_root(self.determine_default_entry_path().as_ref()),
|
||||
)
|
||||
}
|
||||
|
@ -766,8 +776,10 @@ pub struct CompileExtraOpts {
|
|||
pub entry: Option<ImmutPath>,
|
||||
/// Additional input arguments to compile the entry file.
|
||||
pub inputs: ImmutDict,
|
||||
/// will remove later
|
||||
pub font_paths: Vec<PathBuf>,
|
||||
/// Additional font paths.
|
||||
pub font: CompileFontArgs,
|
||||
/// The creation timestamp for various output.
|
||||
pub creation_timestamp: Option<chrono::DateTime<chrono::Utc>>,
|
||||
}
|
||||
|
||||
/// The path pattern that could be substituted.
|
||||
|
|
|
@ -8,6 +8,7 @@ use tinymist_query::{ExportKind, PageSelection};
|
|||
use tokio::sync::mpsc;
|
||||
use typst::{foundations::Smart, layout::Abs, layout::Frame, visualize::Color};
|
||||
use typst_ts_compiler::{EntryReader, EntryState, TaskInputs};
|
||||
use typst_ts_core::TypstDatetime;
|
||||
|
||||
use crate::{
|
||||
actor::{
|
||||
|
@ -196,10 +197,12 @@ impl ExportConfig {
|
|||
static BLANK: Lazy<Frame> = Lazy::new(Frame::default);
|
||||
let first_frame = || doc.pages.first().map(|f| &f.frame).unwrap_or(&*BLANK);
|
||||
Ok(match kind2 {
|
||||
Pdf => {
|
||||
Pdf { creation_timestamp } => {
|
||||
let timestamp =
|
||||
convert_datetime(creation_timestamp.unwrap_or_else(chrono::Utc::now));
|
||||
// todo: Some(pdf_uri.as_str())
|
||||
// todo: timestamp world.now()
|
||||
typst_pdf::pdf(doc, Smart::Auto, None)
|
||||
typst_pdf::pdf(doc, Smart::Auto, timestamp)
|
||||
}
|
||||
Svg { page: First } => typst_svg::svg(first_frame()).into_bytes(),
|
||||
Svg { page: Merged } => typst_svg::svg_merged(doc, Abs::zero()).into_bytes(),
|
||||
|
@ -233,6 +236,19 @@ fn log_err<T>(artifact: anyhow::Result<T>) -> Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert [`chrono::DateTime`] to [`TypstDatetime`]
|
||||
fn convert_datetime(date_time: chrono::DateTime<chrono::Utc>) -> Option<TypstDatetime> {
|
||||
use chrono::{Datelike, Timelike};
|
||||
TypstDatetime::from_ymd_hms(
|
||||
date_time.year(),
|
||||
date_time.month().try_into().ok()?,
|
||||
date_time.day().try_into().ok()?,
|
||||
date_time.hour().try_into().ok()?,
|
||||
date_time.minute().try_into().ok()?,
|
||||
date_time.second().try_into().ok()?,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{borrow::Cow, path::PathBuf, sync::Arc};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use clap::{builder::ValueParser, ArgAction, Parser};
|
||||
use comemo::Prehashed;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -20,7 +21,7 @@ use typst_ts_compiler::{
|
|||
const ENV_PATH_SEP: char = if cfg!(windows) { ';' } else { ':' };
|
||||
|
||||
/// The font arguments for the compiler.
|
||||
#[derive(Debug, Clone, Default, Parser, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Parser, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CompileFontArgs {
|
||||
/// Font paths
|
||||
|
@ -62,6 +63,18 @@ pub struct CompileOnceArgs {
|
|||
/// Font related arguments.
|
||||
#[clap(flatten)]
|
||||
pub font: CompileFontArgs,
|
||||
|
||||
/// The document's creation date formatted as a UNIX timestamp.
|
||||
///
|
||||
/// For more information, see <https://reproducible-builds.org/specs/source-date-epoch/>.
|
||||
#[clap(
|
||||
long = "creation-timestamp",
|
||||
env = "SOURCE_DATE_EPOCH",
|
||||
value_name = "UNIX_TIMESTAMP",
|
||||
value_parser = parse_source_date_epoch,
|
||||
hide(true),
|
||||
)]
|
||||
pub creation_timestamp: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// Compiler feature for LSP world.
|
||||
|
@ -121,3 +134,11 @@ fn parse_input_pair(raw: &str) -> Result<(String, String), String> {
|
|||
let val = val.trim().to_owned();
|
||||
Ok((key, val))
|
||||
}
|
||||
|
||||
/// Parses a UNIX timestamp according to <https://reproducible-builds.org/specs/source-date-epoch/>
|
||||
fn parse_source_date_epoch(raw: &str) -> Result<DateTime<Utc>, String> {
|
||||
let timestamp: i64 = raw
|
||||
.parse()
|
||||
.map_err(|err| format!("timestamp must be decimal integer ({err})"))?;
|
||||
DateTime::from_timestamp(timestamp, 0).ok_or_else(|| "timestamp out of range".to_string())
|
||||
}
|
||||
|
|
|
@ -116,6 +116,14 @@ There is a **global** configuration `tinymist.typstExtraArgs` to pass extra argu
|
|||
typst watch --input=awa=1 --input=abaaba=2 main.typ
|
||||
```
|
||||
|
||||
Supported arguments:
|
||||
- entry file: The last string in the array will be treated as the entry file.
|
||||
- This is used to specify the **default** entry file for the compiler, which may be overridden by other settings.
|
||||
- `--input`: Add a string key-value pair visible through `sys.inputs`.
|
||||
- `--font-path` (environment variable: `TYPST_FONT_PATHS`), Font paths, maybe overriden by `tinymist.fontPaths`.
|
||||
- `--ignore-system-fonts`: Ensures system fonts won't be searched, maybe overriden by `tinymist.systemFonts`.
|
||||
- `--creation-timestamp` (environment variable: `SOURCE_DATE_EPOCH`): The document's creation date formatted as a [UNIX timestamp](https://reproducible-builds.org/specs/source-date-epoch/).
|
||||
|
||||
**Note:** Fix entry to `main.typ` may help multiple-file projects but you may loss diagnostics and autocompletions in unrelated files.
|
||||
|
||||
Note: the arguments has quite low priority, and that may be overridden by other settings.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue