mirror of
https://github.com/typst/typst
synced 2025-08-20 01:48:34 +08:00
Compare commits
6 Commits
dd2eb591db
...
6f52e3b8ca
Author | SHA1 | Date | |
---|---|---|---|
|
6f52e3b8ca | ||
|
36ecbb2c8d | ||
|
51ab5b815c | ||
|
0369b83013 | ||
|
c4f5ba84d1 | ||
|
a41f821fa6 |
@ -1,3 +1,4 @@
|
|||||||
|
use std::ffi::OsStr;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
@ -443,6 +444,27 @@ pub enum OutputFormat {
|
|||||||
Html,
|
Html,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OutputFormat {
|
||||||
|
pub fn from_file_ext(ext: &OsStr) -> Option<Self> {
|
||||||
|
match ext {
|
||||||
|
ext if ext.eq_ignore_ascii_case("pdf") => Some(OutputFormat::Pdf),
|
||||||
|
ext if ext.eq_ignore_ascii_case("png") => Some(OutputFormat::Png),
|
||||||
|
ext if ext.eq_ignore_ascii_case("svg") => Some(OutputFormat::Svg),
|
||||||
|
ext if ext.eq_ignore_ascii_case("html") => Some(OutputFormat::Html),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_file_ext(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Pdf => "pdf",
|
||||||
|
Self::Png => "png",
|
||||||
|
Self::Svg => "svg",
|
||||||
|
Self::Html => "html",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
display_possible_values!(OutputFormat);
|
display_possible_values!(OutputFormat);
|
||||||
|
|
||||||
/// Which format to use for diagnostics.
|
/// Which format to use for diagnostics.
|
||||||
|
@ -96,12 +96,9 @@ impl CompileConfig {
|
|||||||
let output_format = if let Some(specified) = args.format {
|
let output_format = if let Some(specified) = args.format {
|
||||||
specified
|
specified
|
||||||
} else if let Some(Output::Path(output)) = &args.output {
|
} else if let Some(Output::Path(output)) = &args.output {
|
||||||
match output.extension() {
|
match output.extension().and_then(OutputFormat::from_file_ext) {
|
||||||
Some(ext) if ext.eq_ignore_ascii_case("pdf") => OutputFormat::Pdf,
|
Some(format) => format,
|
||||||
Some(ext) if ext.eq_ignore_ascii_case("png") => OutputFormat::Png,
|
None => bail!(
|
||||||
Some(ext) if ext.eq_ignore_ascii_case("svg") => OutputFormat::Svg,
|
|
||||||
Some(ext) if ext.eq_ignore_ascii_case("html") => OutputFormat::Html,
|
|
||||||
_ => bail!(
|
|
||||||
"could not infer output format for path {}.\n\
|
"could not infer output format for path {}.\n\
|
||||||
consider providing the format manually with `--format/-f`",
|
consider providing the format manually with `--format/-f`",
|
||||||
output.display()
|
output.display()
|
||||||
@ -111,19 +108,41 @@ impl CompileConfig {
|
|||||||
OutputFormat::Pdf
|
OutputFormat::Pdf
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = args.output.clone().unwrap_or_else(|| {
|
let output = match args.output.clone() {
|
||||||
let Input::Path(path) = &input else {
|
None => {
|
||||||
panic!("output must be specified when input is from stdin, as guarded by the CLI");
|
let Input::Path(path) = &input else {
|
||||||
};
|
panic!("output must be specified when input is from stdin, as guarded by the CLI");
|
||||||
Output::Path(path.with_extension(
|
};
|
||||||
match output_format {
|
Output::Path(path.with_extension(output_format.as_file_ext()))
|
||||||
OutputFormat::Pdf => "pdf",
|
}
|
||||||
OutputFormat::Png => "png",
|
// Check if a [`Path`] has a trailing `/` (or on `windows` a `\`) character,
|
||||||
OutputFormat::Svg => "svg",
|
// indicating that the output should be written to `{output}/{input_file_name}.{ext}`
|
||||||
OutputFormat::Html => "html",
|
Some(Output::Path(mut path)) if has_trailing_path_separator(&path) => {
|
||||||
},
|
let Input::Path(input) = &input else {
|
||||||
))
|
bail!(
|
||||||
});
|
"can't infer output file when input is from stdin\n\
|
||||||
|
consider providing a full path to write to, or get input from file",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(file_name) = input.file_name() else {
|
||||||
|
panic!("input path must be non-empty, as guarded by the CLI");
|
||||||
|
};
|
||||||
|
|
||||||
|
// create directory if doesn't exist yet
|
||||||
|
std::fs::create_dir_all(&path).map_err(|err| {
|
||||||
|
eco_format!(
|
||||||
|
"failed to create output directory at {path}: {err}",
|
||||||
|
path = path.display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
path.push(file_name);
|
||||||
|
path.set_extension(output_format.as_file_ext());
|
||||||
|
Output::Path(path)
|
||||||
|
}
|
||||||
|
Some(output) => output,
|
||||||
|
};
|
||||||
|
|
||||||
let pages = args.pages.as_ref().map(|export_ranges| {
|
let pages = args.pages.as_ref().map(|export_ranges| {
|
||||||
PageRanges::new(export_ranges.iter().map(|r| r.0.clone()).collect())
|
PageRanges::new(export_ranges.iter().map(|r| r.0.clone()).collect())
|
||||||
@ -162,6 +181,16 @@ impl CompileConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_trailing_path_separator(path: &Path) -> bool {
|
||||||
|
path.as_os_str()
|
||||||
|
.as_encoded_bytes()
|
||||||
|
.last()
|
||||||
|
.copied()
|
||||||
|
.map(Into::<char>::into)
|
||||||
|
.map(std::path::is_separator)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Compile a single time.
|
/// Compile a single time.
|
||||||
///
|
///
|
||||||
/// Returns whether it compiled without errors.
|
/// Returns whether it compiled without errors.
|
||||||
|
@ -20,7 +20,7 @@ use crate::foundations::{
|
|||||||
///
|
///
|
||||||
/// You can call a function by writing a comma-separated list of function
|
/// You can call a function by writing a comma-separated list of function
|
||||||
/// _arguments_ enclosed in parentheses directly after the function name.
|
/// _arguments_ enclosed in parentheses directly after the function name.
|
||||||
/// Additionally, you can pass any number of trailing content blocks arguments
|
/// Additionally, you can pass any number of trailing content block arguments
|
||||||
/// to a function _after_ the normal argument list. If the normal argument list
|
/// to a function _after_ the normal argument list. If the normal argument list
|
||||||
/// would become empty, it can be omitted. Typst supports positional and named
|
/// would become empty, it can be omitted. Typst supports positional and named
|
||||||
/// arguments. The former are identified by position and type, while the latter
|
/// arguments. The former are identified by position and type, while the latter
|
||||||
|
@ -205,7 +205,7 @@
|
|||||||
single or double quotes.
|
single or double quotes.
|
||||||
|
|
||||||
The value is always of type [string]($str). More complex data
|
The value is always of type [string]($str). More complex data
|
||||||
may be parsed manually using functions like [`json.decode`]($json.decode).
|
may be parsed manually using functions like [`json`]($json).
|
||||||
|
|
||||||
- name: sym
|
- name: sym
|
||||||
title: General
|
title: General
|
||||||
|
Loading…
x
Reference in New Issue
Block a user