Skip to main content

sl_map_web/routes/
text.rs

1//! HTTP handler for `POST /api/text/measure`.
2//!
3//! Returns the rendered pixel size of a multi-line text block at a given
4//! font and pixel size, so the render form can check whether a label will
5//! fit a placement slot before the final image is rendered.
6
7use axum::Json;
8use axum::extract::State;
9use serde::{Deserialize, Serialize};
10
11use crate::auth::CurrentUser;
12use crate::error::Error;
13use crate::state::AppState;
14
15/// Request body for `POST /api/text/measure`.
16#[derive(Debug, Deserialize)]
17pub struct MeasureRequest {
18    /// id of the font to measure with (must match one from `GET /api/fonts`).
19    pub font_id: String,
20    /// font size in pixels.
21    pub font_px: f32,
22    /// the text, one entry per line.
23    pub lines: Vec<String>,
24}
25
26/// Response body for `POST /api/text/measure`.
27#[derive(Debug, Serialize)]
28pub struct MeasureResponse {
29    /// rendered width in pixels (the widest line).
30    pub width: u32,
31    /// rendered height in pixels (all lines stacked).
32    pub height: u32,
33}
34
35/// `POST /api/text/measure` — measure the rendered size of multi-line text.
36/// Uses the same font and metrics the renderer uses, so the result matches
37/// what a label would actually occupy.
38///
39/// # Errors
40///
41/// Returns an error if the font size is not a positive finite number, the
42/// font id is unknown, or the font file cannot be read.
43pub async fn measure(
44    _user: CurrentUser,
45    State(state): State<AppState>,
46    Json(req): Json<MeasureRequest>,
47) -> Result<Json<MeasureResponse>, Error> {
48    if !(req.font_px.is_finite() && req.font_px > 0f32) {
49        return Err(Error::BadRequest(format!(
50            "font size must be a positive number of pixels, got {}",
51            req.font_px
52        )));
53    }
54    let font_path = state
55        .fonts
56        .path_for(&req.font_id)
57        .ok_or_else(|| Error::BadRequest(format!("unknown font_id `{}`", req.font_id)))?;
58    let font = sl_map_apis::text::load_font(font_path)?;
59    let scale = ab_glyph::PxScale::from(req.font_px);
60    let (width, height) = sl_map_apis::text::measure_text(scale, &font, &req.lines);
61    Ok(Json(MeasureResponse { width, height }))
62}