download online images for latex mode

master
Kenton Hamaluik 5 years ago
parent c8a477a4e4
commit 167e4a844b

916
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -33,6 +33,8 @@ ws = "0.9"
fern = { version = "0.5", features = ["colored"] }
log = { version = "0.4", features = ["max_level_debug", "release_max_level_info", "std", "serde"] }
ignore = "0.4"
reqwest = "0.9"
md5 = "0.7"
[build-dependencies]
sass-rs = "0.2"

@ -24,9 +24,9 @@ Markdown is formatted usiing [comrak](https://crates.io/crates/comrak) with some
Code is syntax-highlighted using [syntect](https://crates.io/crates/syntect) with the default langauges and the `base16-eighties` colour scheme. Some additional languages above the base list supported by _syntect_ have been aded:
* [`haxe`](https://haxe.org/)
* [`hxml`](https://haxe.org/manual/compiler-usage-hxml.html)
* [`sass`](https://sass-lang.com/documentation/syntax#the-indented-syntax)
* [`scss`](https://sass-lang.com/documentation/syntax)
* [`toml`](https://github.com/toml-lang/toml)
* [haxe](https://haxe.org/)
* [hxml](https://haxe.org/manual/compiler-usage-hxml.html)
* [sass](https://sass-lang.com/documentation/syntax#the-indented-syntax)
* [scss](https://sass-lang.com/documentation/syntax)
* [toml](https://github.com/toml-lang/toml)

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//01-command-line.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/01-commonmark.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/02-syntax-highlighting.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/03-plantuml-diagrams.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/04-katex-formulas.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/05-images.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/06-tables.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/07-task-lists.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//02-markdown/index.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//03-frontmatter.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//04-structure.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//05-customization.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>

@ -12,7 +12,7 @@
<meta property="og:url" content="https://hamaluik.github.io/mkbook//06-how-it-works.html" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body>
@ -118,11 +118,11 @@
<h1><a href="#syntax-highlighting" aria-hidden="true" class="anchor" id="headersyntax-highlighting"></a>Syntax Highlighting</h1>
<p>Code is syntax-highlighted using <a href="https://crates.io/crates/syntect">syntect</a> with the default langauges and the <code>base16-eighties</code> colour scheme. Some additional languages above the base list supported by <em>syntect</em> have been aded:</p>
<ul>
<li><a href="https://haxe.org/"><code>haxe</code></a></li>
<li><a href="https://haxe.org/manual/compiler-usage-hxml.html"><code>hxml</code></a></li>
<li><a href="https://sass-lang.com/documentation/syntax#the-indented-syntax"><code>sass</code></a></li>
<li><a href="https://sass-lang.com/documentation/syntax"><code>scss</code></a></li>
<li><a href="https://github.com/toml-lang/toml"><code>toml</code></a></li>
<li><a href="https://haxe.org/">haxe</a></li>
<li><a href="https://haxe.org/manual/compiler-usage-hxml.html">hxml</a></li>
<li><a href="https://sass-lang.com/documentation/syntax#the-indented-syntax">sass</a></li>
<li><a href="https://sass-lang.com/documentation/syntax">scss</a></li>
<li><a href="https://github.com/toml-lang/toml">toml</a></li>
</ul>

@ -15,14 +15,14 @@
&lt;p&gt;If youre not familiar with &lt;em&gt;mdbook&lt;&#x2f;em&gt;, &lt;em&gt;mkbook&lt;&#x2f;em&gt; is a tool to convert a collection of &lt;a href=&quot;https:&#x2f;&#x2f;commonmark.org&#x2f;&quot;&gt;Markdown&lt;&#x2f;a&gt; files into a static website &#x2f; book which can be published online. It was created to help me write documentation with minimum fuss while presenting it in an easy-to-consume manner.&lt;&#x2f;p&gt;
" />
<meta property="book:author" content="Kenton Hamaluik" />
<meta property="book:release_date" content="2019-12-09T06:12:31.253923271+00:00" />
<meta property="book:release_date" content="2019-12-09T07:02:23.053503163+00:00" />
</head>
<body class="toc">
<header>
<h1>The mkbook Book</h1>
<h2>by Kenton Hamaluik</h2>
<time datetime="2019-12-09T06:12:31.253923271+00:00">Dec 9, 2019</time>
<time datetime="2019-12-09T07:02:23.053503163+00:00">Dec 9, 2019</time>
</header>
<article>
<p><em>mkbook</em> is my simpler alternative to <a href="https://crates.io/crates/mdbook"><em>mdbook</em></a> which is a great tool, however I really dislike some of the decisions they took—such as relying on javascript for highlighting and navigation and including a lot of bells and whistles such as javascript-based search.</p>

@ -1,6 +1,6 @@
use std::path::{Path, PathBuf};
use askama::Template;
use std::fs;
use std::{fs, io};
use comrak::ComrakOptions;
mod filters;
@ -88,13 +88,13 @@ fn format_text<'a>(node: &'a comrak::nodes::AstNode<'a>, output: &mut String) {
}
}
fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, output: &mut String) {
fn format_node<'a, P: AsRef<Path>>(section_offset: u32, dest_path: P, node: &'a comrak::nodes::AstNode<'a>, output: &mut String) {
use comrak::nodes::NodeValue;
match &node.data.borrow().value {
NodeValue::Document => for child in node.children() { format_node(section_offset, child, output); },
NodeValue::Document => for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); },
NodeValue::BlockQuote => {
output.push_str("\\begin{quote}\n");
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
output.push_str("\\end{quote}\n");
},
NodeValue::List(node_list) => {
@ -102,7 +102,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
comrak::nodes::ListType::Bullet => output.push_str("\\begin{itemize}\n"),
comrak::nodes::ListType::Ordered => output.push_str("\\begin{enumerate}\n"),
}
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
match node_list.list_type {
comrak::nodes::ListType::Bullet => output.push_str("\\end{itemize}\n"),
comrak::nodes::ListType::Ordered => output.push_str("\\end{enumerate}\n"),
@ -111,18 +111,18 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
NodeValue::Item(_) => {
output.push_str("\\item ");
let mut item: String = String::default();
for child in node.children() { format_node(section_offset, child, &mut item); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, &mut item); }
output.push_str(item.trim());
output.push_str("\n");
},
NodeValue::DescriptionList => {
output.push_str("\\begin{description}\n");
let mut items: String = String::default();
for child in node.children() { format_node(section_offset, child, &mut items); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, &mut items); }
output.push_str(&items.replace("\n\n\n", "\n").replace("\n\n", "\n"));
output.push_str("\\end{description}\n");
},
NodeValue::DescriptionItem(_) => for child in node.children() { format_node(section_offset, child, output); },
NodeValue::DescriptionItem(_) => for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); },
NodeValue::DescriptionTerm => {
output.push_str("\\item [");
let mut term: String = String::default();
@ -131,7 +131,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
output.push_str("] ");
},
NodeValue::DescriptionDetails => {
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
output.push_str("\n");
},
NodeValue::CodeBlock(node_code_block) => {
@ -164,7 +164,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
},
NodeValue::Paragraph => {
for child in node.children() {
format_node(section_offset, child, output);
format_node(section_offset, dest_path.as_ref(), child, output);
}
output.push_str("\n\n");
},
@ -190,7 +190,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
output.push_str(&escape_text(label));
output.push_str("]{");
let mut definition: String = String::default();
for child in node.children() { format_node(section_offset, child, &mut definition); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, &mut definition); }
output.push_str(definition.trim());
output.push_str("}\n");
},
@ -208,14 +208,14 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
output.push_str("}\n");
let mut rows: String = String::default();
for child in node.children() { format_node(section_offset, child, &mut rows); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, &mut rows); }
output.push_str(rows.trim());
output.push_str("\\end{tabular}\n\\end{center}\n\n");
},
NodeValue::TableRow(header) => {
let row: String = node.children().map(|child| {
let mut column: String = String::default();
format_node(section_offset, child, &mut column);
format_node(section_offset, dest_path.as_ref(), child, &mut column);
column
})
.collect::<Vec<String>>()
@ -229,7 +229,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
output.push_str("\n");
}
},
NodeValue::TableCell => for child in node.children() { format_node(section_offset, child, output); },
NodeValue::TableCell => for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); },
NodeValue::Text(text) => {
if let Ok(text) = std::str::from_utf8(text) {
output.push_str(&escape_text(text));
@ -237,7 +237,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
},
NodeValue::TaskItem(checked) => {
let mut item: String = String::default();
for child in node.children() { format_node(section_offset, child, &mut item); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, &mut item); }
if *checked {
output.push_str(r"$\text{\rlap{$\checkmark$}}\square$ ");
}
@ -269,22 +269,22 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
},
NodeValue::Emph => {
output.push_str("\\emph{");
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
output.push_str("}");
},
NodeValue::Strong => {
output.push_str("\\textbf{");
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
output.push_str("}");
},
NodeValue::Strikethrough => {
output.push_str("\\sout{");
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
output.push_str("}");
},
NodeValue::Superscript => {
output.push_str("\\textsuperscript{");
for child in node.children() { format_node(section_offset, child, output); }
for child in node.children() { format_node(section_offset, dest_path.as_ref(), child, output); }
output.push_str("}");
},
NodeValue::Link(node_link) => {
@ -299,14 +299,70 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
},
NodeValue::Image(node_link) => {
let url = std::str::from_utf8(&node_link.url).expect("valid utf-8");
let title = std::str::from_utf8(&node_link.title).expect("valid utf-8");
if url.starts_with("http://") || url.starts_with("https://") {
// TODO: download images?
log::warn!("skipping image `{}` as we can't download images yet!", url);
//log::warn!("skipping image `{}` as we can't download images yet!", url);
let client = reqwest::Client::new();
let mut res = match client.get(url).send() {
Ok(r) => r,
Err(e) => {
log::error!("failed to download image `{}`, reqwest error: {:?}", url, e);
return;
}
};
if !res.status().is_success() {
log::warn!("failed to download image at url `{}`: error {}: {:?}", url, res.status().as_u16(), res.text());
return;
}
let image_type = match res.headers().get("Content-Type").map(reqwest::header::HeaderValue::to_str) {
Some(Ok("image/jpeg")) => Some("jpg"),
Some(Ok("image/png")) => Some("png"),
Some(Ok("application/pdf")) => Some("pdf"),
Some(Ok("application/postscript")) => Some("eps"),
_ => None,
};
let image_type = match image_type {
Some(t) => t,
None => {
log::warn!("unhandled content-type for url `{}`: {:?}, skipping", url, res.headers().get("Content-Type"));
return;
}
};
// get the md5 of the URL to save as the filename
let filename = md5::compute(url.as_bytes());
let filename = format!("{:x?}.{}", filename, image_type);
let dest = PathBuf::from(dest_path.as_ref()).join(&filename);
log::info!("saving `{}` to `{}`...", url, dest.display());
let f = match fs::File::create(&dest) {
Ok(f) => f,
Err(e) => {
log::error!("failed to create file `{}`: {:?}", dest.display(), e);
return;
}
};
let mut wtr = io::BufWriter::new(f);
if let Err(e) = res.copy_to(&mut wtr) {
log::error!("failed to download `{}` into `{}`: {:?}", url, dest.display(), e);
return;
}
// and finally, now that we've downloaded the image, emit our code
output.push_str("\\begin{figure}[H]\n");
output.push_str("\\centering\n");
output.push_str("\\includegraphics[width=\\maxwidth{\\textwidth}]{");
output.push_str(&filename);
output.push_str("}\n");
output.push_str("\\caption{");
output.push_str(title);
output.push_str("}\n");
output.push_str("\\end{figure}\n");
}
else {
// TODO: make sure the file exists?
let title = std::str::from_utf8(&node_link.title).expect("valid utf-8");
output.push_str("\\begin{figure}[h]\n");
output.push_str("\\centering\n");
output.push_str("\\includegraphics[width=\\maxwidth{\\textwidth}]{");
@ -328,7 +384,7 @@ fn format_node<'a>(section_offset: u32, node: &'a comrak::nodes::AstNode<'a>, ou
}
}
fn format_markdown(section_offset: u32, src: &str) -> Result<String, Box<dyn std::error::Error>> {
fn format_markdown<P: AsRef<Path>>(section_offset: u32, dest_path: P, src: &str) -> Result<String, Box<dyn std::error::Error>> {
let arena = comrak::Arena::new();
let root = comrak::parse_document(
&arena,
@ -336,7 +392,7 @@ fn format_markdown(section_offset: u32, src: &str) -> Result<String, Box<dyn std
&COMRAK_OPTIONS);
let mut latex: String = String::with_capacity(src.len());
format_node(section_offset, &root, &mut latex);
format_node(section_offset, dest_path, &root, &mut latex);
Ok(latex)
}
@ -356,11 +412,11 @@ pub fn build<PIn: AsRef<Path>, POut: AsRef<Path>>(src: PIn, dest: POut) -> Resul
let mut book = super::load_book(&src)?;
// then convert all the markdown
book.description = format_markdown(0, &book.description)?;
book.description = format_markdown(0, &dest_path, &book.description)?;
for chapter in book.chapters.iter_mut() {
chapter.contents = format_markdown(0, &chapter.contents)?;
chapter.contents = format_markdown(0, &dest_path, &chapter.contents)?;
for section in chapter.sections.iter_mut() {
section.contents = format_markdown(1, &section.contents)?;
section.contents = format_markdown(1, &dest_path, &section.contents)?;
}
}

@ -11,6 +11,7 @@ pub const ASSET_DEFAULT_INTRODUCTION: &'static [u8] = include_bytes!(concat!(env
mod cli;
mod models;
mod html;
// TODO: put LaTeX behind a feature
mod latex;
mod extensions;

@ -50,6 +50,9 @@
\def\maxwidth#1{\ifdim\Gin@nat@width>#1 #1\else\Gin@nat@width\fi}
\makeatother
% force floats
\usepackage{float}
\title{ {{ front.title }} }
\author{ {{ front.author }} }
\date{ {{ front.pubdate|human_date }} }
@ -62,12 +65,14 @@
% copyright
\pagestyle{empty}
\topskip0pt
\vspace*{\fill}
\begin{center}
\textcopyright {{ front.pubdate|year }} {{ front.author }}
\footnotesize
\textbf{ {{ front.title }} }\\
\textcopyright {{ front.pubdate|year }} {{ front.author }}{% if front.url.len() > 0 %}\\
\url{ {{ front.url }} }
{% endif %}
\end{center}
\vspace*{\fill}
\tableofcontents

Loading…
Cancel
Save