Media

Placeholders

Placeholder images as SVG: a box of any size with its dimensions or your text in your colours, as markup or a data: URL.

Version 1.0.0 · 2 functions · 0 crates

#images#svg#design#prototypes

Functions

What use placeholder.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use placeholder_svg, placeholder_data_url from placeholder (Quillo 0.6).

placeholder_svg(width: int, height: int, options: any) → str

A placeholder width by height pixels (1 to 10,000) as SVG. options (or null): background ("#e2e8f0"), color of the text ("#64748b"), text (the size, "800 × 600", when left out; "" for none), and border true for a dashed outline.

placeholder_data_url(width: int, height: int, options: any) → str

The same placeholder as a data: URL, for <img src=...> or CSS background-image with no request to make.

Get it

$ quillo install placeholder               # placeholder.qwer into this folder$ quillo install placeholder --example     # and its example, placeholder.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/placeholder.qwer$ curl -O https://quillolang.org/plugins/files/placeholder.qwe

Then, in your program, all of it or only what you call:

main.qwe
use placeholder.qweruse placeholder_svg, placeholder_data_url from placeholder      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]

Example

placeholder.qwe
// Placeholders for a page still waiting for its photos, served at any size.////   quillo run placeholder.qwe      then open http://localhost:8080/use "placeholder.qwer"route GET /ph/:w(int)/:h(int) ? { text: str = "", bg: str = "#e2e8f0" } =    respond(200, placeholder_svg(w, h, text == "" ? { background: bg } : { background: bg, text }), { "content-type": "image/svg+xml", "cache-control": "max-age=86400" })route GET / = <html><body style="font-family: sans-serif">    <h1>New menu, photos soon</h1>    <img src="/ph/600/300" width="600" height="300" alt=""/>    <img src={placeholder_data_url(200, 200, { text: "Soup", background: "#fde68a", color: "#92400e", border: true })} width="200" height="200" alt="Soup"/></body></html>
$ quillo run placeholder.qwe$ curl 'localhost:8080/ph/600/300'$ curl 'localhost:8080/ph/320/180?text=Hero&bg=%23fde68a'$ curl 'localhost:8080/'

Source

placeholder.qwer
//! Placeholder images as SVG: a box of any size with its dimensions or your text written in it, in your colours, as markup or a data: URL for an `<img>`.//! version: 1.0.0//!//! [dependencies]use serde_json::Value;fn esc(s: &str) -> String {    s.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")}// A colour given as "#rgb", "#rrggbb" or a plain CSS name; anything else falls back.fn colour(v: Option<&Value>, fallback: &str) -> String {    match v.and_then(Value::as_str) {        Some(c) if (c.starts_with('#') && [4, 5, 7, 9].contains(&c.len()) && c[1..].chars().all(|x| x.is_ascii_hexdigit())) || (!c.is_empty() && c.len() <= 20 && c.chars().all(|x| x.is_ascii_alphabetic())) => c.to_string(),        _ => fallback.to_string(),    }}/// A placeholder `width` by `height` pixels (1 to 10,000) as SVG. `options` (or null): `background` ("#e2e8f0"), `color` of the text ("#64748b"),/// `text` (the size, "800 × 600", when left out; "" for none), and `border` true for a dashed outline.pub fn placeholder_svg(width: i64, height: i64, options: serde_json::Value) -> String {    let (w, h) = (width.clamp(1, 10_000), height.clamp(1, 10_000));    let bg = colour(options.get("background"), "#e2e8f0");    let fg = colour(options.get("color"), "#64748b");    let text = options.get("text").and_then(Value::as_str).map(str::to_string).unwrap_or_else(|| format!("{} × {}", w, h));    // The text fills about two thirds of the width, and never more than a third of the height.    let chars = text.chars().count().max(1) as f64;    let size = ((w as f64 * 0.66 / (chars * 0.6)).min(h as f64 / 3.0)).max(6.0).round();    let border = if options.get("border").and_then(Value::as_bool) == Some(true) {        format!(r#"<rect x="1" y="1" width="{}" height="{}" fill="none" stroke="{}" stroke-width="2" stroke-dasharray="8 6"/>"#, (w - 2).max(0), (h - 2).max(0), fg)    } else {        String::new()    };    let label = if text.is_empty() { String::new() } else {        format!(r#"<text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" font-family="system-ui, -apple-system, Segoe UI, Roboto, sans-serif" font-size="{}" fill="{}">{}</text>"#, size, fg, esc(&text))    };    format!(r#"<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" viewBox="0 0 {w} {h}"><rect width="{w}" height="{h}" fill="{bg}"/>{border}{label}</svg>"#)}/// The same placeholder as a data: URL, for `<img src=...>` or CSS `background-image` with no request to make.pub fn placeholder_data_url(width: i64, height: i64, options: serde_json::Value) -> String {    let svg = placeholder_svg(width, height, options);    let mut out = String::from("data:image/svg+xml,");    for b in svg.bytes() {        match b {            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b' ' | b'-' | b'_' | b'.' | b'=' | b':' | b'/' | b',' | b';' | b'(' | b')' => out.push(b as char),            _ => out += &format!("%{:02X}", b),        }    }    out}
Thread
88Useful? Like it, or add to its thread below.

Thread 0 posts

On Placeholders: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.