├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── src └── main.rs ├── static └── .gitignore └── templates ├── fail.html └── welcome.hbs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "html2pdf" 3 | version = "0.1.0" 4 | authors = ["rap2hpoutre "] 5 | 6 | [dependencies] 7 | iron = "0.3" 8 | router = "*" 9 | handlebars-iron = "*" 10 | params="0.2.2" 11 | uuid = { version = "0.2", features = ["v4"] } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 raph 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # htmltopdf 2 | html to pdf website using wkhtmltopdf 3 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate iron; 2 | extern crate router; 3 | extern crate handlebars_iron; 4 | extern crate params; 5 | extern crate uuid; 6 | 7 | use iron::prelude::*; 8 | use iron::{AfterMiddleware}; 9 | use iron::status; 10 | use router::{Router, NoRoute}; 11 | use handlebars_iron::{Template, HandlebarsEngine, DirectorySource}; 12 | use std::collections::BTreeMap; 13 | use params::Params; 14 | use params::Value; 15 | use std::process::Command; 16 | use uuid::Uuid; 17 | 18 | struct Custom404; 19 | 20 | impl AfterMiddleware for Custom404 { 21 | fn catch(&self, _: &mut Request, err: IronError) -> IronResult { 22 | println!("Hitting custom 404 middleware"); 23 | if let Some(_) = err.error.downcast::() { 24 | Ok(Response::with((status::NotFound, "Wow. Such 404."))) 25 | } else { 26 | println!("{:?}", err); 27 | Err(err) 28 | } 29 | } 30 | } 31 | 32 | fn main() { 33 | // Templates 34 | let mut hbse = HandlebarsEngine::new(); 35 | hbse.add(Box::new(DirectorySource::new("./templates", ".hbs"))); 36 | if let Err(_) = hbse.reload() { 37 | panic!("Unable to build templates"); 38 | } 39 | 40 | // Routes 41 | let mut router = Router::new(); 42 | router.get("/", welcome); 43 | router.post("/", convert); 44 | 45 | // Full chain 46 | let mut chain = Chain::new(router); 47 | chain.link_after(hbse); 48 | chain.link_after(Custom404); 49 | Iron::new(chain).http("localhost:3000").unwrap(); 50 | 51 | // Welcome route 52 | fn welcome(_: &mut Request) -> IronResult { 53 | let mut resp = Response::new(); 54 | let mut data = BTreeMap::new(); 55 | 56 | data.insert("version".to_string(), "1.0".to_string()); 57 | resp.set_mut(Template::new("welcome", data)).set_mut(status::Ok); 58 | Ok(resp) 59 | } 60 | 61 | // Convert HTML file from request to PDF 62 | fn convert(req: &mut Request) -> IronResult { 63 | let random_name = &format!("{}", Uuid::new_v4()); 64 | let destination_pdf = &{ "./static/".to_string() + random_name + ".pdf" }; 65 | 66 | // Get the HTML file 67 | let html = &match tmp_uploaded_filename(req, "file") { 68 | Some(tmp_file_name) => { 69 | let target_file_name = &{ "./static/".to_string() + random_name + ".html" }; 70 | mv_file(&tmp_file_name, target_file_name); 71 | target_file_name.to_string() 72 | }, 73 | None => { 74 | "./templates/fail.html".to_string() 75 | }, 76 | }; 77 | 78 | // Footer 79 | let footer_html: Option = match tmp_uploaded_filename(req, "footer") { 80 | Some(tmp_file_name) => { 81 | let target_file_name = { "./static/".to_string() + random_name + "-footer.html" }; 82 | mv_file(&tmp_file_name, &target_file_name); 83 | Some(target_file_name) 84 | }, 85 | _ => { 86 | None 87 | }, 88 | }; 89 | 90 | // Convert it to HTML 91 | convert_to_pdf(html, destination_pdf, footer_html); 92 | 93 | Ok(Response::with((status::Ok, format!("http://html2pdf.raph.site/static/{}.pdf\n", random_name).to_string()))) 94 | } 95 | } 96 | 97 | fn convert_to_pdf(html: &str, destination_pdf: &str, footer_html: Option) { 98 | let mut c = Command::new("wkhtmltopdf"); 99 | if let Some(f) = footer_html { 100 | c.arg("--footer-html") 101 | .arg(&f.to_string()); 102 | } 103 | c.arg(html) 104 | .arg(destination_pdf) 105 | .spawn() 106 | .expect("failed to execute process"); 107 | } 108 | 109 | fn tmp_uploaded_filename(req: &mut Request, param_name: &str) -> Option { 110 | match req.get_ref::().unwrap().find(&[param_name]) { 111 | Some(&Value::File(ref file)) => { 112 | Some(file.path().to_str().unwrap().to_string()) 113 | }, 114 | _ => { 115 | None 116 | }, 117 | } 118 | } 119 | 120 | // Move a file (just an mv alias) 121 | fn mv_file(source_name: &str, destination_name: &str) { 122 | let output = Command::new("mv") 123 | .arg(source_name) 124 | .arg(destination_name) 125 | .output() 126 | .expect("failed to execute process"); 127 | println!( 128 | "status: {} stderr: {} stdout: {}", 129 | output.status, 130 | String::from_utf8_lossy(&output.stderr), 131 | String::from_utf8_lossy(&output.stdout) 132 | ); 133 | } 134 | -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /templates/fail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fail 6 | 7 | 8 | Oops. Something went wrong, did you provide some HTML file? 9 | 10 | -------------------------------------------------------------------------------- /templates/welcome.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HTML to PDF 7 | 8 | 38 | 39 | 40 |

HTML to PDF
as a Service

41 |
42 |

43 | Easy to use, just ask curl… 44 |

45 |
curl -F "file=@/path/to/file.html" \
46 |    http://html2pdf.raph.site/
47 |

48 | …and it returns instantly a downloadable link to your PDF 49 |

50 |
http://html2pdf.raph.site/s/b7ae8.pdf
51 |
52 |

53 | Or use that form. 54 |

55 |
56 | 57 |
58 | 59 |
60 |
61 |

62 | Made with 👣 in Rust by @rap2h
63 | It could crash. 64 |

65 | 66 | --------------------------------------------------------------------------------