feat: add no_pdf_tags, ppi, and more pdf_standard to extra args (#2220)
Some checks are pending
tinymist::auto_tag / auto-tag (push) Waiting to run
tinymist::ci / Duplicate Actions Detection (push) Waiting to run
tinymist::ci / Check Clippy, Formatting, Completion, Documentation, and Tests (Linux) (push) Waiting to run
tinymist::ci / Check Minimum Rust version and Tests (Windows) (push) Waiting to run
tinymist::ci / prepare-build (push) Waiting to run
tinymist::ci / announce (push) Blocked by required conditions
tinymist::ci / build (push) Blocked by required conditions
tinymist::gh_pages / build-gh-pages (push) Waiting to run

- `--no-pdf-tags` to disable tagged PDF
- `--ppi` to specify the PPI (pixels per inch) for PNG export
- `--pdf-standard`: add all rest commonly used PDF standards, which was
introduced in `typst` v0.14.0
This commit is contained in:
Myriad-Dreamin 2025-11-04 18:23:07 +08:00 committed by GitHub
parent 7c00eba127
commit a6adbaa926
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 216 additions and 44 deletions

View file

@ -183,6 +183,10 @@ fn shell_build_script(shell: Shell) -> Result<String> {
cmd.push(s);
}
if task.no_pdf_tags {
cmd.push("--no-pdf-tags");
}
if let Some(output) = &task.creation_timestamp {
cmd.push("--creation-timestamp");
cmd.push(output.to_string());

View file

@ -11,7 +11,7 @@ use parking_lot::Mutex;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use tinymist::project::*;
use tinymist::tool::project::{StartProjectResult, start_project};
use tinymist::world::{SourceWorld, with_main};
use tinymist::world::{PngExportArgs, SourceWorld, with_main};
use tinymist_debug::CoverageResult;
use tinymist_project::world::{DiagnosticFormat, system::print_diagnostics};
use tinymist_query::analysis::Analysis;
@ -68,10 +68,6 @@ pub struct TestConfigArgs {
#[clap(long)]
pub update: bool,
/// The argument to export to PNG.
#[clap(flatten)]
pub png: PngExportArgs,
/// Whether to collect coverage.
#[clap(long)]
pub coverage: bool,
@ -137,6 +133,7 @@ pub async fn test_main(args: TestArgs) -> Result<()> {
let config = TestContext {
root,
args: args.config,
png: args.compile.png,
out_file,
analysis: Analysis::default(),
};
@ -266,6 +263,7 @@ struct TestContext {
analysis: Analysis,
root: ImmutPath,
args: TestConfigArgs,
png: PngExportArgs,
out_file: Option<Arc<Mutex<std::fs::File>>>,
}
@ -468,7 +466,7 @@ impl<'a> TestRunner<'a> {
return false;
};
let ppp = self.ctx.args.png.ppi / 72.0;
let ppp = self.ctx.png.ppi / 72.0;
let pixmap = typst_render::render_merged(doc, ppp, Default::default(), None);
let output = pixmap.encode_png().context_ut("cannot encode pixmap");
let output = output.and_then(|output| self.update_example(example, &output, "paged"));

View file

@ -3,6 +3,8 @@ use std::{path::Path, sync::OnceLock};
use clap::ValueHint;
use tinymist_std::{bail, error::prelude::Result};
use tinymist_world::args::PdfExportArgs;
use tinymist_world::args::PngExportArgs;
pub use tinymist_world::args::{CompileFontArgs, CompilePackageArgs};
use crate::PROJECT_ROUTE_USER_ACTION_PRIORITY;
@ -227,7 +229,8 @@ impl TaskCompileArgs {
OutputFormat::Pdf => ProjectTask::ExportPdf(ExportPdfTask {
export,
pages: self.pages.clone(),
pdf_standards: self.pdf.pdf_standard.clone(),
pdf_standards: self.pdf.standard.clone(),
no_pdf_tags: self.pdf.no_tags,
creation_timestamp: None,
}),
OutputFormat::Png => ProjectTask::ExportPng(ExportPngTask {
@ -254,21 +257,3 @@ impl TaskCompileArgs {
})
}
}
/// Specify the PDF export related arguments.
#[derive(Debug, Clone, clap::Parser)]
pub struct PdfExportArgs {
/// Specify the PDF standards that Typst will enforce conformance with.
///
/// If multiple standards are specified, they are separated by commas.
#[arg(long = "pdf-standard", value_delimiter = ',')]
pub pdf_standard: Vec<PdfStandard>,
}
/// Specify the PNG export related arguments.
#[derive(Debug, Clone, clap::Parser)]
pub struct PngExportArgs {
/// Specify the PPI (pixels per inch) to use for PNG export.
#[arg(long = "ppi", default_value_t = 144.0)]
pub ppi: f32,
}

View file

@ -35,14 +35,36 @@ impl<F: CompilerFeat> ExportComputation<F, TypstPagedDocument> for PdfExport {
.pdf_standards
.iter()
.map(|standard| match standard {
tinymist_world::args::PdfStandard::V_1_4 => typst_pdf::PdfStandard::V_1_4,
tinymist_world::args::PdfStandard::V_1_5 => typst_pdf::PdfStandard::V_1_5,
tinymist_world::args::PdfStandard::V_1_6 => typst_pdf::PdfStandard::V_1_6,
tinymist_world::args::PdfStandard::V_1_7 => typst_pdf::PdfStandard::V_1_7,
tinymist_world::args::PdfStandard::V_2_0 => typst_pdf::PdfStandard::V_2_0,
tinymist_world::args::PdfStandard::A_1b => typst_pdf::PdfStandard::A_1b,
tinymist_world::args::PdfStandard::A_1a => typst_pdf::PdfStandard::A_1a,
tinymist_world::args::PdfStandard::A_2b => typst_pdf::PdfStandard::A_2b,
tinymist_world::args::PdfStandard::A_2u => typst_pdf::PdfStandard::A_2u,
tinymist_world::args::PdfStandard::A_2a => typst_pdf::PdfStandard::A_2a,
tinymist_world::args::PdfStandard::A_3b => typst_pdf::PdfStandard::A_3b,
tinymist_world::args::PdfStandard::A_3u => typst_pdf::PdfStandard::A_3u,
tinymist_world::args::PdfStandard::A_3a => typst_pdf::PdfStandard::A_3a,
tinymist_world::args::PdfStandard::A_4 => typst_pdf::PdfStandard::A_4,
tinymist_world::args::PdfStandard::A_4f => typst_pdf::PdfStandard::A_4f,
tinymist_world::args::PdfStandard::A_4e => typst_pdf::PdfStandard::A_4e,
tinymist_world::args::PdfStandard::Ua_1 => typst_pdf::PdfStandard::Ua_1,
})
.collect::<Vec<_>>(),
)
.context_ut("prepare pdf standards")?;
let tagged = !config.no_pdf_tags && config.pages.is_none();
// todo: emit warning diag
if config.pages.is_some() && !config.no_pdf_tags {
log::warn!(
"the resulting PDF will be inaccessible because using --pages implies --no-pdf-tags"
);
}
// todo: Some(pdf_uri.as_str())
// todo: ident option
Ok(Bytes::new(typst_pdf::pdf(
@ -54,6 +76,7 @@ impl<F: CompilerFeat> ExportComputation<F, TypstPagedDocument> for PdfExport {
.map(|pages| exported_page_ranges(pages)),
timestamp: Some(timestamp),
standards,
tagged,
..Default::default()
},
)?))

View file

@ -243,6 +243,12 @@ pub struct ExportPdfTask {
/// conformance with.
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub pdf_standards: Vec<PdfStandard>,
/// By default, even when not producing a `PDF/UA-1` document, a tagged PDF
/// document is written to provide a baseline of accessibility. In some
/// circumstances (for example when trying to reduce the size of a document)
/// it can be desirable to disable tagged PDF.
#[serde(skip_serializing_if = "std::ops::Not::not", default)]
pub no_pdf_tags: bool,
/// The document's creation date formatted as a UNIX timestamp (in seconds).
///
/// For more information, see <https://reproducible-builds.org/specs/source-date-epoch/>.

View file

@ -79,6 +79,14 @@ pub struct CompileOnceArgs {
#[clap(flatten)]
pub package: CompilePackageArgs,
/// Specify the PDF export related arguments.
#[clap(flatten)]
pub pdf: PdfExportArgs,
/// Specify the PNG export related arguments.
#[clap(flatten)]
pub png: PngExportArgs,
/// Enable in-development features that may be changed or removed at any
/// time.
#[arg(long = "features", value_delimiter = ',', env = "TYPST_FEATURES")]
@ -114,12 +122,6 @@ pub struct CompileOnceArgs {
)]
pub creation_timestamp: Option<i64>,
/// Specify the PDF standards that Typst will enforce conformance with.
///
/// If multiple standards are specified, they are separated by commas.
#[arg(long = "pdf-standard", value_delimiter = ',')]
pub pdf_standard: Vec<PdfStandard>,
/// Specify the path to CA certificate file for network access, especially
/// for downloading typst packages.
#[clap(long = "cert", env = "TYPST_CERT", value_name = "CERT_PATH")]
@ -232,6 +234,31 @@ pub fn parse_source_date_epoch(raw: &str) -> Result<i64, String> {
.map_err(|err| format!("timestamp must be decimal integer ({err})"))
}
/// Specify the PDF export related arguments.
#[derive(Debug, Clone, Parser, Default)]
pub struct PdfExportArgs {
/// Specify the PDF standards that Typst will enforce conformance with.
///
/// If multiple standards are specified, they are separated by commas.
#[arg(long = "pdf-standard", value_delimiter = ',')]
pub standard: Vec<PdfStandard>,
/// By default, even when not producing a `PDF/UA-1` document, a tagged PDF
/// document is written to provide a baseline of accessibility. In some
/// circumstances (for example when trying to reduce the size of a document)
/// it can be desirable to disable tagged PDF.
#[arg(long = "no-pdf-tags")]
pub no_tags: bool,
}
/// Specify the PNG export related arguments.
#[derive(Debug, Clone, Parser, Default)]
pub struct PngExportArgs {
/// Specify the PPI (pixels per inch) to use for PNG export.
#[arg(long = "ppi", default_value_t = 144.0)]
pub ppi: f32,
}
macro_rules! display_possible_values {
($ty:ty) => {
impl fmt::Display for $ty {
@ -321,18 +348,74 @@ pub enum ExportTarget {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, ValueEnum, Serialize, Deserialize)]
#[allow(non_camel_case_types)]
pub enum PdfStandard {
/// PDF 1.4.
#[value(name = "1.4")]
#[serde(rename = "1.4")]
V_1_4,
/// PDF 1.5.
#[value(name = "1.5")]
#[serde(rename = "1.5")]
V_1_5,
/// PDF 1.6.
#[value(name = "1.6")]
#[serde(rename = "1.6")]
V_1_6,
/// PDF 1.7.
#[value(name = "1.7")]
#[serde(rename = "1.7")]
V_1_7,
/// PDF 2.0.
#[value(name = "2.0")]
#[serde(rename = "2.0")]
V_2_0,
/// PDF/A-1b.
#[value(name = "a-1b")]
#[serde(rename = "a-1b")]
A_1b,
/// PDF/A-1a.
#[value(name = "a-1a")]
#[serde(rename = "a-1a")]
A_1a,
/// PDF/A-2b.
#[value(name = "a-2b")]
#[serde(rename = "a-2b")]
A_2b,
/// PDF/A-2u.
#[value(name = "a-2u")]
#[serde(rename = "a-2u")]
A_2u,
/// PDF/A-2a.
#[value(name = "a-2a")]
#[serde(rename = "a-2a")]
A_2a,
/// PDF/A-3b.
#[value(name = "a-3b")]
#[serde(rename = "a-3b")]
A_3b,
/// PDF/A-3u.
#[value(name = "a-3u")]
#[serde(rename = "a-3u")]
A_3u,
/// PDF/A-3a.
#[value(name = "a-3a")]
#[serde(rename = "a-3a")]
A_3a,
/// PDF/A-4.
#[value(name = "a-4")]
#[serde(rename = "a-4")]
A_4,
/// PDF/A-4f.
#[value(name = "a-4f")]
#[serde(rename = "a-4f")]
A_4f,
/// PDF/A-4e.
#[value(name = "a-4e")]
#[serde(rename = "a-4e")]
A_4e,
/// PDF/UA-1.
#[value(name = "ua-1")]
#[serde(rename = "ua-1")]
Ua_1,
}
display_possible_values!(PdfStandard);

View file

@ -30,6 +30,11 @@ struct ExportPdfOpts {
creation_timestamp: Option<String>,
/// A PDF standard that Typst can enforce conformance with.
pdf_standard: Option<Vec<PdfStandard>>,
/// By default, even when not producing a `PDF/UA-1` document, a tagged PDF
/// document is written to provide a baseline of accessibility. In some
/// circumstances (for example when trying to reduce the size of a document)
/// it can be desirable to disable tagged PDF.
pub no_pdf_tags: bool,
}
#[derive(Debug, Clone, Default, Deserialize)]
@ -106,6 +111,7 @@ impl ServerState {
export,
pages: opts.pages,
pdf_standards: pdf_standards.unwrap_or_default(),
no_pdf_tags: opts.no_pdf_tags,
creation_timestamp,
}),
args,
@ -198,7 +204,7 @@ impl ServerState {
pub fn export_png(&mut self, mut args: Vec<JsonValue>) -> ScheduleResult {
let opts = get_arg_or_default!(args[1] as ExportPngOpts);
let ppi = opts.ppi.unwrap_or(144.);
let ppi = opts.ppi.or_else(|| self.config.ppi()).unwrap_or(144.);
let ppi = ppi
.try_into()
.context("cannot convert ppi")

View file

@ -447,7 +447,9 @@ impl Config {
root_dir: args.root.as_ref().map(|r| r.as_path().into()),
font: args.font,
package: args.package,
pdf_standard: args.pdf_standard,
pdf_standard: args.pdf.standard,
no_pdf_tags: args.pdf.no_tags,
ppi: args.png.ppi,
features: args.features,
creation_timestamp: args.creation_timestamp,
cert: args.cert.as_deref().map(From::from),
@ -562,6 +564,7 @@ impl Config {
export,
pages: None, // todo: set pages
pdf_standards: self.pdf_standards().unwrap_or_default(),
no_pdf_tags: self.no_pdf_tags(),
creation_timestamp: self.creation_timestamp(),
}),
count_words: self.notify_status,
@ -661,6 +664,18 @@ impl Config {
Some(self.typst_extra_args.as_ref()?.pdf_standard.clone())
}
/// Determines the no pdf tags.
pub fn no_pdf_tags(&self) -> bool {
self.typst_extra_args
.as_ref()
.is_some_and(|x| x.no_pdf_tags)
}
/// Determines the ppi.
pub fn ppi(&self) -> Option<f32> {
Some(self.typst_extra_args.as_ref()?.ppi)
}
/// Determines the creation timestamp.
pub fn creation_timestamp(&self) -> Option<i64> {
self.typst_extra_args.as_ref()?.creation_timestamp
@ -971,6 +986,13 @@ pub struct TypstExtraArgs {
/// One (or multiple comma-separated) PDF standards that Typst will enforce
/// conformance with.
pub pdf_standard: Vec<PdfStandard>,
/// The PPI (pixels per inch) to use for PNG export.
pub ppi: f32,
/// By default, even when not producing a `PDF/UA-1` document, a tagged PDF
/// document is written to provide a baseline of accessibility. In some
/// circumstances (for example when trying to reduce the size of a document)
/// it can be desirable to disable tagged PDF.
pub no_pdf_tags: bool,
/// The creation timestamp for various outputs (in seconds).
pub creation_timestamp: Option<i64>,
/// The path to the certification file.
@ -1063,6 +1085,7 @@ mod tests {
config.typst_extra_args,
Some(TypstExtraArgs {
root_dir: Some(ImmutPath::from(root_path)),
ppi: 144.0,
..TypstExtraArgs::default()
})
);

View file

@ -648,6 +648,7 @@ impl Default for ExportUserConfig {
},
pages: None,
pdf_standards: vec![],
no_pdf_tags: false,
creation_timestamp: None,
}),
count_words: false,

View file

@ -179,15 +179,34 @@ fn test_help_compile() {
Page numbers are one-indexed and correspond to physical page numbers in the document
(therefore not being affected by the document's page counter).
--pdf-standard <PDF_STANDARD>
--pdf-standard <STANDARD>
Specify the PDF standards that Typst will enforce conformance with.
If multiple standards are specified, they are separated by commas.
Possible values:
- 1.4: PDF 1.4
- 1.5: PDF 1.5
- 1.6: PDF 1.6
- 1.7: PDF 1.7
- 2.0: PDF 2.0
- a-1b: PDF/A-1b
- a-1a: PDF/A-1a
- a-2b: PDF/A-2b
- a-2u: PDF/A-2u
- a-2a: PDF/A-2a
- a-3b: PDF/A-3b
- a-3u: PDF/A-3u
- a-3a: PDF/A-3a
- a-4: PDF/A-4
- a-4f: PDF/A-4f
- a-4e: PDF/A-4e
- ua-1: PDF/UA-1
--no-pdf-tags
By default, even when not producing a `PDF/UA-1` document, a tagged PDF document is
written to provide a baseline of accessibility. In some circumstances (for example when
trying to reduce the size of a document) it can be desirable to disable tagged PDF
--ppi <PPI>
Specify the PPI (pixels per inch) to use for PNG export
@ -291,6 +310,40 @@ fn test_help_preview() {
[env: TYPST_PACKAGE_CACHE_PATH=REDACTED]
--pdf-standard <STANDARD>
Specify the PDF standards that Typst will enforce conformance with.
If multiple standards are specified, they are separated by commas.
Possible values:
- 1.4: PDF 1.4
- 1.5: PDF 1.5
- 1.6: PDF 1.6
- 1.7: PDF 1.7
- 2.0: PDF 2.0
- a-1b: PDF/A-1b
- a-1a: PDF/A-1a
- a-2b: PDF/A-2b
- a-2u: PDF/A-2u
- a-2a: PDF/A-2a
- a-3b: PDF/A-3b
- a-3u: PDF/A-3u
- a-3a: PDF/A-3a
- a-4: PDF/A-4
- a-4f: PDF/A-4f
- a-4e: PDF/A-4e
- ua-1: PDF/UA-1
--no-pdf-tags
By default, even when not producing a `PDF/UA-1` document, a tagged PDF document is
written to provide a baseline of accessibility. In some circumstances (for example when
trying to reduce the size of a document) it can be desirable to disable tagged PDF
--ppi <PPI>
Specify the PPI (pixels per inch) to use for PNG export
[default: 144]
--features <FEATURES>
Enable in-development features that may be changed or removed at any time
@ -308,16 +361,6 @@ fn test_help_preview() {
```bash tinymist compile --input foo=bar ```
--pdf-standard <PDF_STANDARD>
Specify the PDF standards that Typst will enforce conformance with.
If multiple standards are specified, they are separated by commas.
Possible values:
- 1.7: PDF 1.7
- a-2b: PDF/A-2b
- a-3b: PDF/A-3b
--cert <CERT_PATH>
Specify the path to CA certificate file for network access, especially for downloading
typst packages