107 lines
2.4 KiB
Rust
107 lines
2.4 KiB
Rust
use crate::{admin::station::typst::TypstWrapperWorld, url, AppState, Station};
|
|
use axum::{
|
|
extract::State,
|
|
http::{header, StatusCode},
|
|
response::IntoResponse,
|
|
routing::get,
|
|
Router,
|
|
};
|
|
use sqlx::SqlitePool;
|
|
use std::{fmt::Write, sync::Arc};
|
|
use typst_pdf::PdfOptions;
|
|
|
|
pub(crate) async fn station_pdf(stations: Vec<Station>) -> Vec<u8> {
|
|
let mut content = String::from(
|
|
r#"
|
|
#import "@preview/cades:0.3.0": qr-code
|
|
|
|
#set page(paper: "a4")
|
|
#set page(margin: 0pt)
|
|
|
|
#let card_width = 105mm // A4 width (210mm) divided by 2
|
|
#let card_height = 74.25mm // A4 height (297mm) divided by 4
|
|
|
|
// Custom function to create a card with title and QR code for a URL
|
|
#let create_url_card(title, url) = {
|
|
box(
|
|
width: card_width,
|
|
height: card_height,
|
|
stroke: (
|
|
paint: black,
|
|
dash: "loosely-dashed",
|
|
thickness: 0.5pt
|
|
),
|
|
[
|
|
#align(center + horizon)[
|
|
#text(weight: "bold", size: 14pt)[Station #title]
|
|
#qr-code(url, width: 4cm)
|
|
#text(size: 8pt)[#link(url)]
|
|
]
|
|
]
|
|
)
|
|
}
|
|
|
|
// Function to create a grid of cards
|
|
#let create_card_grid(cards) = {
|
|
grid(
|
|
columns: 2,
|
|
rows: 4,
|
|
gutter: 0pt,
|
|
..cards
|
|
)
|
|
}
|
|
|
|
#let cards = (
|
|
"#,
|
|
);
|
|
|
|
for station in stations {
|
|
if !station.crewless() {
|
|
let name = station.name;
|
|
let link = format!("{}/s/{}/{}", url(), station.id, station.pw);
|
|
write!(content, "create_url_card(\"Station {name}\", \"{link}\"),").unwrap();
|
|
}
|
|
}
|
|
|
|
write!(
|
|
content,
|
|
r#")
|
|
|
|
#create_card_grid(cards)"#
|
|
)
|
|
.unwrap();
|
|
|
|
// Create world with content.
|
|
let world = TypstWrapperWorld::new("../".to_owned(), content);
|
|
|
|
// Render document
|
|
let document = typst::compile(&world)
|
|
.output
|
|
.expect("Error compiling typst");
|
|
|
|
// Output to pdf
|
|
typst_pdf::pdf(&document, &PdfOptions::default()).expect("Error exporting PDF")
|
|
}
|
|
|
|
async fn index(State(db): State<Arc<SqlitePool>>) -> impl IntoResponse {
|
|
let stations = Station::all(&db).await;
|
|
let pdf = station_pdf(stations).await;
|
|
|
|
(
|
|
StatusCode::OK,
|
|
[
|
|
(header::CONTENT_TYPE, "application/pdf"),
|
|
(
|
|
header::CONTENT_DISPOSITION,
|
|
"attachment; filename=\"stations.pdf\"",
|
|
),
|
|
],
|
|
pdf,
|
|
)
|
|
.into_response()
|
|
}
|
|
|
|
pub(super) fn routes() -> Router<AppState> {
|
|
Router::new().route("/print", get(index))
|
|
}
|