SVG to PNG
SVG drawn to PNG at the size you ask, text included with a built-in font: link previews, email images, icons and charts.
Version 1.0.0 · 3 functions · 2 crates
Functions
What use svg_png.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use svg_to_png, svg_file_to_png from svg_png (Quillo 0.6).
svg_to_png(svg: str, out: str, width: int) → anyor an errorDraws the SVG text svg into a PNG at out (folders made), width pixels wide with the height to keep its shape (0 for its own size); transparent where the SVG draws nothing. { width, height }. Up to 8,000 pixels a side; pictures inside it load only from data: URLs.
svg_file_to_png(from: str, out: str, width: int) → anyor an errorThe same from an SVG file at from.
svg_size(svg: str) → anyor an errorThe SVG's own size, { width, height } in pixels, from its width and height or its viewBox.
Get it
$ quillo install svg_png # svg_png.qwer into this folder$ quillo install svg_png --example # and its example, svg_png.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/svg_png.qwer$ curl -O https://quillolang.org/plugins/files/svg_png.qweThen, in your program, all of it or only what you call:
use svg_png.qweruse svg_to_png, svg_file_to_png from svg_png // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
resvg = "0.45"dejavu = "2.37"Example
// A badge drawn as SVG and served as PNG, for an email that cannot show SVG.//// quillo run svg_png.qwe then open http://localhost:8080/badge.pnguse "svg_png.qwer"fn badge(label, value, color) = `<svg xmlns="http://www.w3.org/2000/svg" width="180" height="28" viewBox="0 0 180 28"> <rect width="180" height="28" rx="6" fill="#333"/> <rect x="90" width="90" height="28" rx="6" fill="${color}"/> <rect x="90" width="10" height="28" fill="${color}"/> <text x="45" y="19" font-size="14" font-family="sans-serif" fill="#fff" text-anchor="middle">${escape(label)}</text> <text x="135" y="19" font-size="14" font-family="sans-serif" fill="#fff" text-anchor="middle" font-weight="bold">${escape(value)}</text></svg>`route GET /badge.png ? { label: str = "tests", value: str = "passing" } { svg_to_png(badge(label, value, "#2e9e5b"), "data/badges/badge.png", 360)? file("data/badges/badge.png")}route GET /sizes { let svg = badge("build", "ok", "#2e9e5b") { own: svg_size(svg)?, png: svg_to_png(svg, "data/badges/build.png", 0)?, double: svg_to_png(svg, "data/badges/build@2x.png", 360)? }}$ quillo run svg_png.qwe$ curl 'localhost:8080/sizes'$ curl 'localhost:8080/badge.png'Source
//! SVG drawn to PNG at the size you ask, text included (a font is built in): link previews, email images, icons and charts for places that take no SVG.//! version: 1.0.0//!//! [dependencies]//! resvg = "0.45"//! dejavu = "2.37"use resvg::tiny_skia;use resvg::usvg;use serde_json::{json, Value};use std::sync::{Arc, OnceLock};const MAX_SIDE: u32 = 8000;const MAX_SVG: usize = 32 * 1024 * 1024;// The system's fonts, and DejaVu Sans built in for servers that have none: loaded once.fn fonts() -> Arc<usvg::fontdb::Database> { static DB: OnceLock<Arc<usvg::fontdb::Database>> = OnceLock::new(); DB.get_or_init(|| { let mut db = usvg::fontdb::Database::new(); db.load_system_fonts(); db.load_font_data(dejavu::sans::regular().to_vec()); db.load_font_data(dejavu::sans::bold().to_vec()); db.load_font_data(dejavu::sans::oblique().to_vec()); db.load_font_data(dejavu::sans_mono::regular().to_vec()); db.set_sans_serif_family("DejaVu Sans"); db.set_monospace_family("DejaVu Sans Mono"); Arc::new(db) }) .clone()}fn tree(svg: &str) -> Result<usvg::Tree, String> { if svg.len() > MAX_SVG { return Err("the SVG is larger than 32 MB".into()); } let mut opt = usvg::Options::default(); opt.fontdb = fonts(); opt.font_family = "DejaVu Sans".into(); // Pictures inside the SVG come only from data: URLs, never from files or the network. opt.resources_dir = None; opt.image_href_resolver = usvg::ImageHrefResolver { resolve_data: usvg::ImageHrefResolver::default_data_resolver(), resolve_string: Box::new(|_, _| None) }; usvg::Tree::from_str(svg, &opt).map_err(|e| format!("the SVG cannot be read: {}", e))}fn render(svg: &str, out: &str, width: i64) -> Result<Value, String> { let t = tree(svg)?; let size = t.size(); let (sw, sh) = (size.width(), size.height()); let w = if width > 0 { width as f32 } else { sw }; let scale = w / sw; let (pw, ph) = (w.round() as u32, (sh * scale).round() as u32); if pw == 0 || ph == 0 || pw > MAX_SIDE || ph > MAX_SIDE { return Err(format!("{}×{} is outside 1 to {} pixels a side", pw, ph, MAX_SIDE)); } let mut pixmap = tiny_skia::Pixmap::new(pw, ph).ok_or("cannot make the picture")?; resvg::render(&t, tiny_skia::Transform::from_scale(scale, scale), &mut pixmap.as_mut()); if let Some(dir) = std::path::Path::new(out).parent().filter(|d| !d.as_os_str().is_empty()) { std::fs::create_dir_all(dir).map_err(|e| format!("{}: {}", dir.display(), e))?; } pixmap.save_png(out).map_err(|e| format!("{}: {}", out, e))?; Ok(json!({ "width": pw, "height": ph }))}/// Draws the SVG text `svg` into a PNG at `out` (folders made), `width` pixels wide with the height to keep its shape (0 for its own size); transparent where the SVG draws nothing./// `{ width, height }`. Up to 8,000 pixels a side; pictures inside it load only from data: URLs.pub fn svg_to_png(svg: &str, out: &str, width: i64) -> Result<Value, String> { render(svg, out, width)}/// The same from an SVG file at `from`.pub fn svg_file_to_png(from: &str, out: &str, width: i64) -> Result<Value, String> { let svg = std::fs::read_to_string(from).map_err(|e| format!("{}: {}", from, e))?; render(&svg, out, width)}/// The SVG's own size, `{ width, height }` in pixels, from its width and height or its viewBox.pub fn svg_size(svg: &str) -> Result<Value, String> { let s = tree(svg)?.size(); Ok(json!({ "width": s.width(), "height": s.height() }))}Related
sparkline.qwer · 1.0.0Tiny inline charts from a list of numbers: SVG line and bar sparklines for table cells and sentences, and the same in text (▁▃▆█).6 views · 8 downloadsSVG charts chart_svg.qwer · 1.0.0Line and bar charts as SVG from your data, with axes, a legend and labels, light or dark, for emails, PDFs and reports.6 views · 6 downloadsHeatmaps heatmap.qwer · 1.0.0Heatmaps as PNG: clicks or sightings blurred into a density map over a screenshot or a plain background, and tables of numbers as coloured grids.9 views · 9 downloadsPlaceholders placeholder.qwer · 1.0.0Placeholder images as SVG: a box of any size with its dimensions or your text in your colours, as markup or a data: URL.8 views · 8 downloadsQR codes 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 downloadsThread 0 posts
On SVG to PNG: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.