From 09c36dbe700e20bbacba81d6e36abf1410c48130 Mon Sep 17 00:00:00 2001 From: Andrew Cowie Date: Wed, 27 May 2026 14:47:45 +1000 Subject: [PATCH 1/4] Bump dependencies --- Cargo.lock | 32 ++++++++++++++++---------------- Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ca2890..1baf9c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bstr" @@ -91,9 +91,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", ] @@ -234,9 +234,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linux-raw-sys" @@ -246,9 +246,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lsp-server" @@ -287,9 +287,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "nu-ansi-term" @@ -302,9 +302,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "once_cell" @@ -389,7 +389,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys", @@ -437,9 +437,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -493,7 +493,7 @@ dependencies = [ [[package]] name = "technique" -version = "0.5.6" +version = "0.5.7" dependencies = [ "clap", "ignore", diff --git a/Cargo.toml b/Cargo.toml index 03346d8..0e170f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "technique" -version = "0.5.6" +version = "0.5.7" edition = "2021" description = "A domain specific language for procedures." authors = [ "Andrew Cowie" ] From 5aca1767edc2db77ba9ad5c3acfef830ea45a944 Mon Sep 17 00:00:00 2001 From: Andrew Cowie Date: Wed, 27 May 2026 23:33:48 +1000 Subject: [PATCH 2/4] Add --paper option to render command --- src/main.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index a2242c5..a84c5db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ +use clap::builder::{PossibleValue, TypedValueParser}; use clap::value_parser; use clap::{Arg, ArgAction, Command}; use owo_colors::OwoColorize; use std::io::IsTerminal; use std::path::Path; +use std::str::FromStr; use tracing::debug; use tracing_subscriber::{self, EnvFilter}; @@ -31,6 +33,89 @@ enum Phase { Translation, } +// Page dimensions in millimetres +#[derive(Clone, Debug)] +struct PaperSize { + width: f64, + height: f64, +} + +impl FromStr for PaperSize { + type Err = String; + + fn from_str(s: &str) -> Result { + let (width, height) = match s { + "a4" => (210.0, 297.0), + "a5" => (148.0, 210.0), + "letter" => (215.9, 279.4), + _ => { + let (w, h) = s + .split_once(|c: char| c == 'x' || c == '×') + .ok_or_else(|| format!("invalid paper size '{}'", s))?; + let w: f64 = w + .trim() + .parse() + .map_err(|e| format!("invalid width '{}': {}", w, e))?; + let h: f64 = h + .trim() + .parse() + .map_err(|e| format!("invalid height '{}': {}", h, e))?; + (w, h) + } + }; + Ok(PaperSize { width, height }) + } +} + +// Custom clap parser so the named presets show up under "possible values" in +// help output, while still allowing the parser to accept free-form width x +// height dimensions. +#[derive(Clone)] +struct PaperSizeParser; + +impl TypedValueParser for PaperSizeParser { + type Value = PaperSize; + + fn parse_ref( + &self, + cmd: &Command, + arg: Option<&Arg>, + value: &std::ffi::OsStr, + ) -> Result { + let s = value + .to_str() + .ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8).with_cmd(cmd))?; + s.parse::() + .map_err(|e| { + let mut err = + clap::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd); + if let Some(arg) = arg { + err.insert( + clap::error::ContextKind::InvalidArg, + clap::error::ContextValue::String(arg.to_string()), + ); + } + err.insert( + clap::error::ContextKind::InvalidValue, + clap::error::ContextValue::String(s.to_string()), + ); + err.insert( + clap::error::ContextKind::Custom, + clap::error::ContextValue::String(e), + ); + err + }) + } + + fn possible_values(&self) -> Option + '_>> { + Some(Box::new( + ["a4", "a5", "letter", "WxH"] + .into_iter() + .map(PossibleValue::new), + )) + } +} + fn main() { const VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); @@ -158,6 +243,15 @@ fn main() { .action(ArgAction::Set) .help("Path to a Typst template file for rendering."), ) + .arg( + Arg::new("paper") + .short('p') + .long("paper") + .value_name("SIZE") + .value_parser(PaperSizeParser) + .action(ArgAction::Set) + .help("Paper size for the rendered output. You can use one of the predefined well-known sizes, or specify explicit dimensions in millimetres (for example \"140x210\"). If a paper size is not specified, the template's default is used."), + ) .arg( Arg::new("keep") .short('k') @@ -477,8 +571,23 @@ fn main() { None => None, }; + let (paper_width, paper_height) = match submatches.get_one::("paper") { + Some(p) => (p.width, p.height), + None => template + .default_paper() + .unwrap_or((210.0, 297.0)), + }; + + debug!(paper_width, paper_height); + let markup = template.markup(&technique); - let document = templating::assemble(template.domain(), &markup, custom); + let document = templating::assemble( + template.domain(), + &markup, + custom, + paper_width, + paper_height, + ); let keep = *submatches .get_one::("keep") From 94ceb863eb062558f3a721721c62e788c3bfdca7 Mon Sep 17 00:00:00 2001 From: Andrew Cowie Date: Wed, 27 May 2026 23:44:52 +1000 Subject: [PATCH 3/4] Allow default paper size in templates --- src/templating/mod.rs | 16 ++++++++++++++-- src/templating/nasa_esa_iss.rs | 4 ++++ src/templating/nasa_esa_iss.typ | 1 - src/templating/template.rs | 7 +++++++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/templating/mod.rs b/src/templating/mod.rs index a9d4f93..9215514 100644 --- a/src/templating/mod.rs +++ b/src/templating/mod.rs @@ -28,12 +28,24 @@ pub use template::Template; /// customize the output template. Because the import is * any functions that /// the user redefines in their template will override the names from the /// default. -pub fn assemble(domain: &str, markup: &str, custom: Option<&str>) -> String { +/// +/// `width` and `height` are the page dimensions in millimetres, supplied via +/// the `--paper` CLI option. +pub fn assemble( + domain: &str, + markup: &str, + custom: Option<&str>, + width: f64, + height: f64, +) -> String { let mut doc = format!("#import \".{}.typ\": *\n", domain); if let Some(path) = custom { doc.push_str(&format!("#import \"/{}\": *\n", path)); } - doc.push_str("\n#show: template\n\n"); + doc.push_str(&format!( + "\n#set page(width: {}mm, height: {}mm)\n\n#show: template\n\n", + width, height + )); doc.push_str(markup); doc } diff --git a/src/templating/nasa_esa_iss.rs b/src/templating/nasa_esa_iss.rs index 0a0c0ef..78027cb 100644 --- a/src/templating/nasa_esa_iss.rs +++ b/src/templating/nasa_esa_iss.rs @@ -27,4 +27,8 @@ impl Template for NasaEsaIss { fn domain(&self) -> &str { "nasa-esa-iss" } + + fn default_paper(&self) -> Option<(f64, f64)> { + Some((148.0, 210.0)) + } } diff --git a/src/templating/nasa_esa_iss.typ b/src/templating/nasa_esa_iss.typ index 081bc8a..edd7e6a 100644 --- a/src/templating/nasa_esa_iss.typ +++ b/src/templating/nasa_esa_iss.typ @@ -199,7 +199,6 @@ #let template(body) = { set page( - paper: "a5", margin: (top: 1.5cm, bottom: 1.5cm, left: 1.5cm, right: 1.5cm), numbering: "1", number-align: center + bottom, diff --git a/src/templating/template.rs b/src/templating/template.rs index 028bc6c..9321566 100644 --- a/src/templating/template.rs +++ b/src/templating/template.rs @@ -13,4 +13,11 @@ pub trait Template { /// Return the domain name (used for the template filename on disk). fn domain(&self) -> &str; + + /// The page size, as (width, height) in millimetres, that this domain + /// prefers when the user has not specified `--paper`. Return `None` to + /// fall back to the global default. + fn default_paper(&self) -> Option<(f64, f64)> { + None + } } From 5182abff4c1d44bfbca784cf399470d03e03cff1 Mon Sep 17 00:00:00 2001 From: Andrew Cowie Date: Thu, 28 May 2026 10:10:46 +1000 Subject: [PATCH 4/4] Improve help text --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index a84c5db..74928a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -250,7 +250,7 @@ fn main() { .value_name("SIZE") .value_parser(PaperSizeParser) .action(ArgAction::Set) - .help("Paper size for the rendered output. You can use one of the predefined well-known sizes, or specify explicit dimensions in millimetres (for example \"140x210\"). If a paper size is not specified, the template's default is used."), + .help("Paper size for the rendered output. You can use the name of one of the well-known standard sizes, or give explicit dimensions for the width and height in millimetres (\"140x210\", for example). If a paper size is not specified, the template's default will be used."), ) .arg( Arg::new("keep")