1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
#[macro_use]
extern crate rocket;
use cached::proc_macro::cached;
use quick_xml::events::BytesText;
use quick_xml::Writer;
use rocket::http::{ContentType, Status};
use std::io;
use std::io::{Cursor, Write};
use std::process::{Command, Stdio};
fn to_input_svg(text: &str) -> anyhow::Result<Vec<u8>> {
let mut writer = Writer::new(Cursor::new(vec![]));
writer
.create_element("svg")
.with_attribute(("xmlns", "http://www.w3.org/2000/svg"))
.write_inner_content(|writer| {
writer
.create_element("style")
.write_text_content(BytesText::new("text { font-family: 'linja pona'; }"))?;
writer
.create_element("text")
.write_text_content(BytesText::new(text))?;
Ok(())
})?;
Ok(writer.into_inner().into_inner())
}
fn to_final_svg(text: &str) -> anyhow::Result<String> {
let input_svg = to_input_svg(text)?;
let mut inkscape_command = Command::new("inkscape")
.args([
"--pipe",
"--export-type=svg",
"--export-area-drawing",
"--export-plain-svg",
"--export-text-to-path",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
inkscape_command
.stdin
.take()
.unwrap()
.write_all(&input_svg)?;
let mut svgo_command = Command::new("svgo")
.args(["--config", "./svgo.config.js", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
io::copy(
&mut inkscape_command.stdout.take().unwrap(),
&mut svgo_command.stdin.take().unwrap(),
)?;
let inkscape_result = inkscape_command.wait()?;
assert!(inkscape_result.success());
let svgo_result = svgo_command.wait_with_output()?;
assert!(svgo_result.status.success());
let result = String::from_utf8(svgo_result.stdout)?;
Ok(result)
}
#[cached]
fn get_result(text: String) -> Result<(ContentType, String), (Status, String)> {
match to_final_svg(&text) {
Ok(svg) => Ok((ContentType::SVG, svg)),
Err(e) => Err((Status::InternalServerError, e.to_string())),
}
}
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
#[get("/render/<text>")]
fn render(text: &str) -> Result<(ContentType, String), (Status, String)> {
get_result(text.to_string())
}
#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
let _rocket = rocket::build()
.mount("/", routes![index, render])
.launch()
.await?;
Ok(())
}
|