use headless_chrome::Browser; use headless_chrome::types::PrintToPdfOptions; use nanobyte_opentelemetry::rocket::{TracingSpan, RequestId, OtelReqwestClient}; use nanobyte_tera::l18n::LanguageDescription; use ovlach_data::cv::data::CV; use reqwest::Client; use rocket::fs::NamedFile; use ::rocket::{State, http::Status}; use ::rocket::get; use tempfile::NamedTempFile; use tera::Context; use tracing::{info_span, error, debug}; use crate::DefaultPerson; use crate::{chromium::rocket::BrowserHolder, tools::{tera::NanoTera, pdf::PdfStream, rocket::RequestLanguage}, services::cv::fetch_cv_data_from_backend, CVBackendConfig}; // TODO: request-id fn generate_pdf(browser: Browser, file: &NamedTempFile) -> Vec { let tab = browser.new_tab().unwrap(); let path = format!("file://{}", file.path().to_str().unwrap()); info_span!("open_pdf").in_scope(|| { debug!("Render pdf from {}", &path); tab.navigate_to(&path).unwrap().wait_until_navigated().unwrap(); }); let options = PrintToPdfOptions{ margin_bottom: Some(0.0), margin_left: Some(0.0), margin_right: Some(0.0), margin_top: Some(0.0), print_background: Some(true), //paper_width: Some(29.7), //paper_height: Some(21.0), ..PrintToPdfOptions::default() }; //thread::sleep(Duration::from_secs(10)); let bytes = info_span!("print_pdf").in_scope(|| { tab.print_to_pdf(Some(options)).unwrap() }); return bytes; } #[tracing::instrument] fn render_template(template_name: &str, file: &NamedTempFile, tera: NanoTera, cv: CV, language: String) { // TODO: handle errors let mut tera_context = Context::new(); tera_context.insert("cv", &cv); tera_context.insert("lang", &LanguageDescription::new(&language, "ovlach_pdf")); match tera.0.render_to("two_column.html.tera", &tera_context, file) { Ok(_) => { debug!("Rendered template to {}", file.path().to_str().unwrap()); }, Err(e) => { error!("Error rendering template {}: {}", &template_name, e); } } } #[get("/cv///output.pdf")] pub async fn render_pdf_cv(username: &str, tera: NanoTera, tracing: TracingSpan, request_client: OtelReqwestClient, cv_config: &State, language: RequestLanguage, browser: BrowserHolder, default_person: &State) -> Result { let entered_span = tracing.0.enter(); match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &default_person.inner().default_person_name, &request_client.0).await { Ok(cv_data) => { let file = tempfile::Builder::new().suffix(".html").tempfile().unwrap(); render_template("two_column", &file, tera, cv_data, language.language); let span = info_span!("render_pdf", username = username); let pdf = span.in_scope(||{ generate_pdf(browser.browser, &file) }); drop(entered_span); Ok(PdfStream::new(pdf)) }, Err(e) => { error!("Error fetching cv data: {:?}", e); Err(Status::InternalServerError) } } } /// Route only for debuging #[get("/cv///output.html")] pub async fn render_html_cv(username: &str, tera: NanoTera, tracing: TracingSpan, request_client: OtelReqwestClient, cv_config: &State, language: RequestLanguage, default_person: &State) -> Result { let _ = tracing.0.enter(); match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &default_person.inner().default_person_name, &request_client.0).await { Ok(cv_data) => { let file = tempfile::Builder::new().suffix(".html").tempfile().unwrap(); render_template("two_column", &file, tera, cv_data, language.language); Ok(NamedFile::open(file.path()).await.unwrap()) }, Err(e) => { error!("Error fetching cv data: {:?}", e); Err(Status::InternalServerError) } } }