|
| 1 | +use std::io::{self, Write}; |
| 2 | +use std::path::{Path, PathBuf}; |
| 3 | +use std::process; |
| 4 | + |
| 5 | +use clap::Parser; |
| 6 | +use termcolor::{ColorChoice, ColorSpec, StandardStream, WriteColor}; |
| 7 | + |
| 8 | +#[derive(Debug, Parser)] |
| 9 | +#[clap(about, version)] |
| 10 | +struct Args { |
| 11 | + /// Path to read SVG file from. |
| 12 | + input: PathBuf, |
| 13 | + /// Path to write PDF file to. |
| 14 | + output: Option<PathBuf>, |
| 15 | + /// The number of SVG pixels per PDF points. |
| 16 | + #[clap(long, default_value = "72.0")] |
| 17 | + dpi: f64, |
| 18 | +} |
| 19 | + |
| 20 | +fn main() { |
| 21 | + if let Err(msg) = run() { |
| 22 | + print_error(&msg).unwrap(); |
| 23 | + process::exit(1); |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +fn run() -> Result<(), String> { |
| 28 | + let args = Args::parse(); |
| 29 | + |
| 30 | + // Determine output path. |
| 31 | + let name = |
| 32 | + Path::new(args.input.file_name().ok_or("Input path does not point to a file")?); |
| 33 | + let output = args.output.unwrap_or_else(|| name.with_extension("pdf")); |
| 34 | + |
| 35 | + // Load source file. |
| 36 | + let svg = |
| 37 | + std::fs::read_to_string(&args.input).map_err(|_| "Failed to load SVG file")?; |
| 38 | + |
| 39 | + // Convert string to SVG. |
| 40 | + let mut options = usvg::Options::default(); |
| 41 | + options.fontdb = fontdb::Database::new(); |
| 42 | + options.fontdb.load_system_fonts(); |
| 43 | + let tree = |
| 44 | + usvg::Tree::from_str(&svg, &options.to_ref()).map_err(|err| err.to_string())?; |
| 45 | + |
| 46 | + // Convert SVG to PDF. |
| 47 | + let mut options = svg2pdf::Options::default(); |
| 48 | + options.dpi = args.dpi; |
| 49 | + let pdf = svg2pdf::convert_tree(&tree, options); |
| 50 | + |
| 51 | + // Write output file. |
| 52 | + std::fs::write(output, pdf).map_err(|_| "Failed to write PDF file")?; |
| 53 | + |
| 54 | + Ok(()) |
| 55 | +} |
| 56 | + |
| 57 | +fn print_error(msg: &str) -> io::Result<()> { |
| 58 | + let mut w = StandardStream::stderr(ColorChoice::Always); |
| 59 | + |
| 60 | + let mut color = ColorSpec::new(); |
| 61 | + color.set_fg(Some(termcolor::Color::Red)); |
| 62 | + color.set_bold(true); |
| 63 | + w.set_color(&color)?; |
| 64 | + write!(w, "error")?; |
| 65 | + |
| 66 | + w.reset()?; |
| 67 | + writeln!(w, ": {msg}.") |
| 68 | +} |
0 commit comments