diff --git a/Cargo.toml b/Cargo.toml index 883614c..814fde9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] png = "0.11.0" -cairo-rs = { version = "0.17.0", default-features = false, features = ["png", "freetype"] } +cairo-rs = { version = "0.17.0", default-features = false, features = ["png", "svg", "freetype"] } axum = "0.6.10" tokio = { version = "1.0", features = ["full"] } rand = "0.8.5" diff --git a/src/main.rs b/src/main.rs index 3940a42..f4ba9be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use axum::{http::Request, response::Response, Router}; +use axum::{http::Request, response::Response, routing::get, Router}; use std::{net::SocketAddr, time::Duration}; use text::EmbeddedFonts; use tower_http::{catch_panic::CatchPanicLayer, trace::TraceLayer}; @@ -21,7 +21,8 @@ async fn main() { }; let app = Router::new() .nest("/", routes::static_files::routes()) - .nest("/logo", routes::logo::routes()) + .route("/logo.png", get(routes::logo::png)) + .route("/logo.svg", get(routes::logo::svg)) .nest("/favicon.ico", routes::favicon::routes()) .layer( TraceLayer::new_for_http() diff --git a/src/routes/logo.rs b/src/routes/logo.rs index 62841d1..6c64dfc 100644 --- a/src/routes/logo.rs +++ b/src/routes/logo.rs @@ -5,7 +5,7 @@ use axum::{ routing::get, Router, }; -use cairo::{freetype::Face, Context, FontFace, Format, ImageSurface}; +use cairo::{freetype::Face, Context, FontFace, Format, ImageSurface, SvgSurface}; use serde::Deserialize; use crate::{color::Color, polygon, text::DrawableText, SharedState}; @@ -27,23 +27,32 @@ enum LogoOrientation { } #[derive(Deserialize, Default)] -struct ImageProperties { +pub struct ImageProperties { #[serde(default)] variant: LogoVariant, #[serde(default)] orientation: LogoOrientation, } -fn create_surface(properties: &ImageProperties) -> ImageSurface { - let (width, height) = match (&properties.variant, &properties.orientation) { +fn get_surface_size(properties: &ImageProperties) -> (i32, i32) { + match (&properties.variant, &properties.orientation) { (LogoVariant::NoText, _) => (400, 400), (_, LogoOrientation::Landscape) => (2127, 591), (_, LogoOrientation::Portrait) => (1654, 1654), - }; + } +} +fn create_image_surface(properties: &ImageProperties) -> ImageSurface { + let (width, height) = get_surface_size(properties); ImageSurface::create(Format::ARgb32, width, height).unwrap() } +fn create_svg_surface(properties: &ImageProperties) -> SvgSurface { + let (width, height) = get_surface_size(properties); + + SvgSurface::new::(width as f64, height as f64, None).unwrap() +} + fn draw_logo(context: &Context, properties: &ImageProperties) { let (logo_coordinates, logo_outer_radius, logo_inner_radius) = match (&properties.variant, &properties.orientation) { @@ -123,38 +132,21 @@ fn draw_text( } #[axum_macros::debug_handler] -async fn handler( +pub async fn png( Query(properties): Query, State(state): State, ) -> impl axum::response::IntoResponse { - let start = Instant::now(); let (font_regular, font_light) = state.fonts.get().await; - println!("{:?}", start.elapsed()); - let start = Instant::now(); // cannot use await after this, because surface does not implement Send - - let surface = create_surface(&properties); + let surface = create_image_surface(&properties); let context = Context::new(&surface).unwrap(); - println!("{:?}", (surface.width(), surface.height())); - - println!("{:?}", start.elapsed()); - let start = Instant::now(); - draw_logo(&context, &properties); - println!("{:?}", start.elapsed()); - - let start = Instant::now(); - draw_text(&context, &font_regular, &font_light, &properties); - println!("{:?}", start.elapsed()); - let start = Instant::now(); - let mut data: Vec = Vec::new(); surface.write_to_png(&mut data).unwrap(); - println!("{:?}", start.elapsed()); ( axum::response::AppendHeaders([(axum::http::header::CONTENT_TYPE, "image/png")]), @@ -162,6 +154,24 @@ async fn handler( ) } -pub fn routes() -> Router { - Router::::new().route("/", get(handler)) +#[axum_macros::debug_handler] +pub async fn svg( + Query(properties): Query, + State(state): State, +) -> impl axum::response::IntoResponse { + let (font_regular, font_light) = state.fonts.get().await; + + // cannot use await after this, because surface does not implement Send + let surface = create_svg_surface(&properties); + let context = Context::new(&surface).unwrap(); + + draw_logo(&context, &properties); + draw_text(&context, &font_regular, &font_light, &properties); + + let mut data: Vec = Vec::new(); + surface.write_to_png(&mut data).unwrap(); + ( + axum::response::AppendHeaders([(axum::http::header::CONTENT_TYPE, "image/png")]), + data, + ) }