Media

Initials avatars

Round avatars with a person's initials as SVG, the colour from the name so each keeps theirs, the text readable on it.

Version 1.0.0 · 3 functions · 0 crates

#avatars#users#svg

Functions

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

avatar_initials(name: str) → str

The initials of name, at most two letters in capitals: "Ada Lovelace" is "AL", "grace" is "G", "Jean-Luc Picard" is "JP"; an email gives those of its name part ("ada.l@x" is "AL"). "?" for a name with no letters.

avatar_color(name: str) → str

The background colour for name, one of twelve, always the same for the same name (any case, spaces trimmed).

avatar_svg(name: str, size: int, options: any) → str

An avatar for name as SVG, size pixels square (16 to 1024). options (or null): shape "circle" (the default), "rounded" or "square", background and color as "#rrggbb" to choose them yourself, and title false to leave out the tooltip with the name.

Get it

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

Or with curl:

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

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

main.qwe
use avatar_initials.qweruse avatar_initials, avatar_color from avatar_initials      // Quillo 0.6: only these

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

[dependencies]

Example

avatar_initials.qwe
// A team page with an initials avatar for everyone who has not uploaded a photo.////   quillo run avatar_initials.qwe      then open http://localhost:8080/use "avatar_initials.qwer"let team = ["Ada Lovelace", "grace hopper", "Jean-Luc Picard", "alan.turing@example.com", "Edsger"]route GET /avatar/:name = respond(200, avatar_svg(name, 64, null), { "content-type": "image/svg+xml", "cache-control": "max-age=86400" })route GET / = <html><body style="font-family: sans-serif">    {for n in team => <div style="display: flex; gap: 8px; align-items: center; margin: 4px">{raw(avatar_svg(n, 40, { shape: "rounded" }))} {n}</div>}</body></html>route GET /initials = team | map((n) => { name: n, initials: avatar_initials(n), color: avatar_color(n) })
$ quillo run avatar_initials.qwe$ curl 'localhost:8080/initials'$ curl 'localhost:8080/avatar/Ada%20Lovelace'

Source

avatar_initials.qwer
//! Round avatars with a person's initials, as SVG: the colour comes from the name, so each person keeps theirs, and the text stays readable on it.//! version: 1.0.0//!//! [dependencies]use serde_json::Value;// Mid-tone colours on which white text reads (4.5:1 or better).const COLOURS: [&str; 12] = ["#1d4ed8", "#b91c1c", "#047857", "#7c3aed", "#be185d", "#0e7490", "#a16207", "#4338ca", "#15803d", "#c2410c", "#6d28d9", "#0f766e"];fn esc(s: &str) -> String {    s.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")}/// The initials of `name`, at most two letters in capitals: "Ada Lovelace" is "AL", "grace" is "G", "Jean-Luc Picard" is "JP"; an email gives those of its name part ("ada.l@x" is "AL")./// "?" for a name with no letters.pub fn avatar_initials(name: &str) -> String {    let base = name.split('@').next().unwrap_or("");    let words: Vec<&str> = base.split(|c: char| c.is_whitespace() || c == '.' || c == '_' || c == '-' || c == '+').filter(|w| w.chars().any(char::is_alphanumeric)).collect();    let first = |w: &str| w.chars().find(|c| c.is_alphanumeric()).map(|c| c.to_uppercase().collect::<String>()).unwrap_or_default();    let s = match words.as_slice() {        [] => "?".to_string(),        [one] => first(one),        [a, .., b] => format!("{}{}", first(a), first(b)),    };    if s.is_empty() { "?".into() } else { s }}/// The background colour for `name`, one of twelve, always the same for the same name (any case, spaces trimmed).pub fn avatar_color(name: &str) -> String {    let mut h: u32 = 2166136261;    for b in name.trim().to_lowercase().bytes() {        h ^= b as u32;        h = h.wrapping_mul(16777619);    }    COLOURS[(h % COLOURS.len() as u32) as usize].to_string()}/// An avatar for `name` as SVG, `size` pixels square (16 to 1024). `options` (or null): `shape` "circle" (the default), "rounded" or "square",/// `background` and `color` as "#rrggbb" to choose them yourself, and `title` false to leave out the tooltip with the name.pub fn avatar_svg(name: &str, size: i64, options: serde_json::Value) -> String {    let s = size.clamp(16, 1024);    let pick = |k: &str| options.get(k).and_then(Value::as_str).filter(|c| c.starts_with('#') && [4, 7].contains(&c.len()) && c[1..].chars().all(|x| x.is_ascii_hexdigit())).map(str::to_string);    let bg = pick("background").unwrap_or_else(|| avatar_color(name));    let fg = pick("color").unwrap_or_else(|| "#ffffff".into());    let initials = avatar_initials(name);    let shape = match options.get("shape").and_then(Value::as_str).unwrap_or("circle") {        "square" => format!(r#"<rect width="{s}" height="{s}" fill="{bg}"/>"#),        "rounded" => format!(r#"<rect width="{s}" height="{s}" rx="{}" fill="{bg}"/>"#, s / 5),        _ => format!(r#"<circle cx="{h}" cy="{h}" r="{h}" fill="{bg}"/>"#, h = s as f64 / 2.0),    };    let font = if initials.chars().count() > 1 { s as f64 * 0.4 } else { s as f64 * 0.48 };    let title = if options.get("title").and_then(Value::as_bool) == Some(false) { String::new() } else { format!("<title>{}</title>", esc(name.trim())) };    format!(r#"<svg xmlns="http://www.w3.org/2000/svg" width="{s}" height="{s}" viewBox="0 0 {s} {s}" role="img">{title}{shape}<text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="{font:.0}" font-weight="600" fill="{fg}">{}</text></svg>"#, esc(&initials))}
Thread
67Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.