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
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) → strA 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) → strThe 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.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/placeholder.qwer$ curl -O https://quillolang.org/plugins/files/placeholder.qweThen, in your program, all of it or only what you call:
use placeholder.qweruse placeholder_svg, placeholder_data_url from placeholder // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
Example
// 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 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('&', "&").replace('<', "<").replace('>', ">").replace('"', """)}// 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}Related
qr.qwer · 1.0.0QR codes as SVG for links, tickets, Wi-Fi and two-factor set-up, in any colours, or drawn as text.10 views · 7 downloadsSprite sheets sprites.qwer · 1.0.0Sprite sheets: small pictures packed into one PNG with a CSS class or a JSON frame for each, retina sheets too, and SVG icons gathered into one SVG of symbols to draw with <use href>.8 views · 6 downloadsSVG to PNG svg_png.qwer · 1.0.0SVG drawn to PNG at the size you ask, text included with a built-in font: link previews, email images, icons and charts.7 views · 7 downloadsAnimated GIF gif.qwer · 1.0.0Animated GIFs made from a list of pictures with the delay and size you choose, and GIFs read back: frames, size and length.6 views · 7 downloadsASCII art ascii_art.qwer · 1.0.0Pictures turned into ASCII art: characters from light to dark, plain for terminals and emails, or coloured as HTML.6 views · 10 downloadsAudio waveform audio_wave.qwer · 1.0.0A WAV file's waveform for an audio player: the loudness of each slice from 0 to 1, or drawn as an SVG of bars.6 views · 7 downloadsThread 0 posts
On Placeholders: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.