forked from tforgione/md2pdf
Compare commits
No commits in common. "1867256af51074321590ab4adcd9c28ec75a41e3" and "9eabebe99f3aa34c1ca8aa910c96b1ed4ce5d68e" have entirely different histories.
1867256af5
...
9eabebe99f
849
Cargo.lock
generated
849
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,18 @@
|
||||
[package]
|
||||
name = "md2pdf"
|
||||
version = "0.0.3"
|
||||
version = "0.0.2"
|
||||
authors = ["Thomas Forgione <thomas@forgione.fr>"]
|
||||
description = "A small utility to convert markdown files to pdf exploiting tectonic."
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://gitea.tforgione.fr/tforgione/md2pdf"
|
||||
readme = "README.md"
|
||||
cargo = "1.64"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tectonic = "0.9"
|
||||
tectonic = "0.5.2"
|
||||
pulldown-cmark = "0.5.3"
|
||||
clap = { version = "4.0.7", features = ["cargo"] }
|
||||
|
||||
@ -22,7 +23,3 @@ path = "src/lib.rs"
|
||||
[[bin]]
|
||||
name = "md2pdf"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.3.0"
|
||||
lopdf = "0.27.0"
|
||||
|
107
src/lib.rs
107
src/lib.rs
@ -1,6 +1,6 @@
|
||||
use pulldown_cmark::{Event, Parser, Tag};
|
||||
|
||||
pub const LATEX_HEADER: &str = r#"\documentclass{scrartcl}
|
||||
pub const LATEX_HEADER:&str = r#"\documentclass{scrartcl}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{listings}
|
||||
@ -50,11 +50,11 @@ pub fn markdown_to_latex(markdown: String) -> String {
|
||||
match event {
|
||||
Event::Start(Tag::Header(level)) => {
|
||||
output.push_str("\\");
|
||||
for _ in 1..level {
|
||||
for _ in 1 .. level {
|
||||
output.push_str("sub");
|
||||
}
|
||||
output.push_str("section{");
|
||||
}
|
||||
},
|
||||
Event::End(Tag::Header(_)) => output.push_str("}\n"),
|
||||
|
||||
Event::Start(Tag::Emphasis) => output.push_str("\\emph{"),
|
||||
@ -73,11 +73,11 @@ pub fn markdown_to_latex(markdown: String) -> String {
|
||||
output.push_str("\\href{");
|
||||
output.push_str(&*url);
|
||||
output.push_str("}{");
|
||||
}
|
||||
},
|
||||
|
||||
Event::End(Tag::Link(_, _, _)) => {
|
||||
output.push_str("}");
|
||||
}
|
||||
},
|
||||
|
||||
Event::Start(Tag::Image(_, path, title)) => {
|
||||
output.push_str("\\begin{figure}\n");
|
||||
@ -88,32 +88,32 @@ pub fn markdown_to_latex(markdown: String) -> String {
|
||||
output.push_str("\\caption{");
|
||||
output.push_str(&*title);
|
||||
output.push_str("}\n\\end{figure}\n");
|
||||
}
|
||||
},
|
||||
|
||||
Event::Start(Tag::Item) => output.push_str("\\item "),
|
||||
Event::End(Tag::Item) => output.push_str("\n"),
|
||||
|
||||
Event::Start(Tag::CodeBlock(lang)) => {
|
||||
if !lang.is_empty() {
|
||||
if ! lang.is_empty() {
|
||||
output.push_str("\\begin{lstlisting}[language=");
|
||||
output.push_str(&*lang);
|
||||
output.push_str("]\n");
|
||||
} else {
|
||||
output.push_str("\\begin{lstlisting}\n");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Event::End(Tag::CodeBlock(_)) => {
|
||||
output.push_str("\n\\end{lstlisting}\n");
|
||||
}
|
||||
},
|
||||
|
||||
Event::Text(t) => {
|
||||
output.push_str(&*t);
|
||||
}
|
||||
},
|
||||
|
||||
Event::SoftBreak => {
|
||||
output.push('\n');
|
||||
}
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
@ -128,88 +128,3 @@ pub fn markdown_to_pdf(markdown: String) -> Result<Vec<u8>, tectonic::Error> {
|
||||
tectonic::latex_to_pdf(markdown_to_latex(markdown))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{markdown_to_latex, markdown_to_pdf};
|
||||
use lopdf::Document;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::io::Cursor;
|
||||
|
||||
const MARKDOWN_IN: &str = r#"# First title
|
||||
Some content
|
||||
## Second level
|
||||
Text
|
||||
[link](https://example.com)
|
||||
**Bold**
|
||||
__Italic__
|
||||
"#;
|
||||
const LATEXT_OUT: &str = r#"\documentclass{scrartcl}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{listings}
|
||||
\usepackage{xcolor}
|
||||
\definecolor{colKeys}{rgb}{0,0.5,0}
|
||||
\definecolor{colIdentifier}{rgb}{0,0,0}
|
||||
\definecolor{colComments}{rgb}{0,0.5,1}
|
||||
\definecolor{colString}{rgb}{0.6,0.1,0.1}
|
||||
\definecolor{colBackground}{rgb}{0.95,0.95,1}
|
||||
\lstset{%configuration de listings
|
||||
float=hbp,%
|
||||
basicstyle=\ttfamily\small,%
|
||||
%
|
||||
identifierstyle=\color{colIdentifier}, %
|
||||
keywordstyle=\color{colKeys}, %
|
||||
stringstyle=\color{colString}, %
|
||||
commentstyle=\color{colComments}\textit, %
|
||||
%
|
||||
backgroundcolor=\color{colBackground},%
|
||||
%
|
||||
columns=flexible, %
|
||||
tabsize=2, %
|
||||
frame=trbl, %
|
||||
%frameround=tttt,%
|
||||
extendedchars=true, %
|
||||
showspaces=false, %
|
||||
showstringspaces=false, %
|
||||
numbers=left, %
|
||||
numberstyle=\tiny, %
|
||||
breaklines=true, %
|
||||
breakautoindent=true, %
|
||||
captionpos=b,%
|
||||
xrightmargin=0.2cm, %
|
||||
xleftmargin=0.2cm
|
||||
}
|
||||
\begin{document}
|
||||
\section{First title}
|
||||
Some content\subsection{Second level}
|
||||
Text
|
||||
\href{https://example.com}{link}
|
||||
\textbf{Bold}
|
||||
\textbf{Italic}
|
||||
\end{document}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn test_md_to_latex() {
|
||||
let output = markdown_to_latex(MARKDOWN_IN.to_string());
|
||||
assert_eq!(LATEXT_OUT, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_latex_to_pdf() {
|
||||
let output = markdown_to_pdf(MARKDOWN_IN.to_string());
|
||||
|
||||
match output {
|
||||
Ok(data) => {
|
||||
let mut file = Cursor::new(data);
|
||||
match Document::load_from(&mut file) {
|
||||
Ok(doc) => {
|
||||
assert_eq!("1.5", doc.version);
|
||||
}
|
||||
Err(_) => assert!(true),
|
||||
}
|
||||
}
|
||||
Err(_) => assert!(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
src/main.rs
84
src/main.rs
@ -1,10 +1,10 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use std::path::PathBuf;
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use clap::{crate_authors, crate_description, crate_name, crate_version, Arg, Command};
|
||||
use clap::{crate_authors, crate_description, crate_name, crate_version, Command, Arg};
|
||||
|
||||
use md2pdf::{markdown_to_latex, markdown_to_pdf};
|
||||
|
||||
@ -17,40 +17,36 @@ macro_rules! unwrap {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
let matches = Command::new(crate_name!())
|
||||
.bin_name(crate_name!())
|
||||
.version(crate_version!())
|
||||
.author(crate_authors!("\n"))
|
||||
.about(crate_description!())
|
||||
.arg(
|
||||
Arg::new("INPUT")
|
||||
.long("input")
|
||||
.short('i')
|
||||
.help("Input markdown files")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(PathBuf)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("OUTPUT")
|
||||
.long("output")
|
||||
.short('o')
|
||||
.help("Output tex or pdf file")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(PathBuf)),
|
||||
)
|
||||
.arg(Arg::new("INPUT")
|
||||
.long("input")
|
||||
.short('i')
|
||||
.help("Input markdown files")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(PathBuf))
|
||||
)
|
||||
.arg(Arg::new("OUTPUT")
|
||||
.long("output")
|
||||
.short('o')
|
||||
.help("Output tex or pdf file")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(PathBuf))
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let input_path = matches.get_one::<PathBuf>("INPUT").unwrap();
|
||||
let mut content = String::new();
|
||||
let mut input = unwrap!(File::open(input_path), "couldn't open input file");
|
||||
unwrap!(
|
||||
input.read_to_string(&mut content),
|
||||
"couldn't read file content"
|
||||
);
|
||||
unwrap!(input.read_to_string(&mut content), "couldn't read file content");
|
||||
|
||||
let output_path = matches.get_one::<PathBuf>("OUTPUT").unwrap();
|
||||
let output_path_ext = output_path.extension().and_then(OsStr::to_str);
|
||||
@ -60,33 +56,37 @@ fn main() {
|
||||
Some("tex") => {
|
||||
let tex = markdown_to_latex(content);
|
||||
unwrap!(output.write(tex.as_bytes()), "couldn't write output file");
|
||||
}
|
||||
Some("pdf") => match markdown_to_pdf(content) {
|
||||
Ok(data) => match output.write(&data) {
|
||||
Ok(_) => {
|
||||
exit(0);
|
||||
}
|
||||
},
|
||||
Some("pdf") => {
|
||||
match markdown_to_pdf(content) {
|
||||
Ok(data) => {
|
||||
match output.write(&data) {
|
||||
Ok(_) => {
|
||||
exit(0);
|
||||
},
|
||||
Err(error) => {
|
||||
eprintln!(
|
||||
"error while writing file: {}", error
|
||||
);
|
||||
exit(1);
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
eprintln!("error while writing file: {}", error);
|
||||
eprintln!(
|
||||
"error while compiling latex: {}", error.description()
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
eprintln!("error while compiling latex: {}", error.description());
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
Some(ext) => {
|
||||
eprintln!(
|
||||
"unknown file format ({}) for output: {}",
|
||||
ext,
|
||||
output_path.display()
|
||||
);
|
||||
eprintln!("unknown file format ({}) for output: {}", ext, output_path.display());
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
eprintln!("unknown file format for output: {}", output_path.display());
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user