Files

PDF documents

Simple PDFs: receipts, letters, tickets and reports as headings and paragraphs, wrapped and flowed onto A4 or Letter pages.

Version 1.0.0 · 1 function · 1 crate

#pdf#receipts#invoices#reports

Functions

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

pdf_write(path: str, title: str, size: str, blocks: list) → intor an error

Writes a PDF to path: title (the document's), page size "A4" or "Letter", and blocks, each { text, size?, bold? } (size in points, 11 by default), wrapped and flowed onto new pages. The standard fonts cover Latin-1 text. The number of pages.

Get it

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

Or with curl:

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

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

main.qwe
use pdf.qweruse pdf_write from pdf      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
printpdf = "0.7"

Example

pdf.qwe
// A receipt as a PDF download.////   quillo run pdf.qwe      then open http://localhost:8080/receipt.pdfuse "pdf.qwer"route GET /receipt.pdf {    pdf_write("receipt.pdf", "Receipt 4521", "A4", [        { text: "Quillo Café", size: 22, bold: true },        { text: "Receipt 4521 · 20 November 2026" },        { text: "2 × Flat white          € 7.00\n1 × Croissant            € 3.20" },        { text: "Total                    € 10.20", bold: true },        { text: "Thank you, and see you soon.", size: 9 },    ])?    file("receipt.pdf")}route GET /pages = { pages: pdf_write("long.pdf", "Long", "Letter", for i in range(120) => { text: `Line ${i + 1} of a long report.` })? }
$ quillo run pdf.qwe$ curl 'localhost:8080/receipt.pdf'$ curl 'localhost:8080/pages'

Source

pdf.qwer
//! Simple PDF documents: receipts, letters, tickets and reports, as headings and paragraphs on A4 or Letter pages, in the standard fonts.//! version: 1.0.0//!//! [dependencies]//! printpdf = "0.7"use printpdf::{BuiltinFont, IndirectFontRef, Mm, PdfDocument};use serde_json::Value;use std::io::BufWriter;/// Points to millimetres, and how wide a line of `size`-point Helvetica may be, in characters, across `width` mm.fn chars_per_line(size: f64, width: f64) -> usize {    ((width / (size * 0.3528 * 0.5)).floor() as usize).max(10)}fn wrap(text: &str, max: usize) -> Vec<String> {    let mut lines = Vec::new();    for para in text.split('\n') {        let mut line = String::new();        for word in para.split_whitespace() {            if !line.is_empty() && line.chars().count() + 1 + word.chars().count() > max {                lines.push(std::mem::take(&mut line));            }            if !line.is_empty() { line.push(' '); }            line.push_str(word);        }        lines.push(line);    }    lines}/// Writes a PDF to `path`: `title` (the document's), page `size` "A4" or "Letter", and `blocks`, each `{ text, size?, bold? }` (size in points, 11 by default), wrapped and flowed onto new pages. The standard fonts cover Latin-1 text. The number of pages.pub fn pdf_write(path: &str, title: &str, size: &str, blocks: Vec<serde_json::Value>) -> Result<i64, String> {    let (w, h): (f32, f32) = if size.eq_ignore_ascii_case("letter") { (215.9, 279.4) } else { (210.0, 297.0) };    let margin: f32 = 20.0;    let (doc, p1, l1) = PdfDocument::new(title, Mm(w), Mm(h), "text");    let regular: IndirectFontRef = doc.add_builtin_font(BuiltinFont::Helvetica).map_err(|e| e.to_string())?;    let bold: IndirectFontRef = doc.add_builtin_font(BuiltinFont::HelveticaBold).map_err(|e| e.to_string())?;    let mut layer = doc.get_page(p1).get_layer(l1);    let (mut y, mut pages) = (h - margin, 1);    for b in &blocks {        let text = b.get("text").and_then(Value::as_str).unwrap_or("");        let pt = b.get("size").and_then(Value::as_f64).unwrap_or(11.0).clamp(4.0, 72.0);        let font = if b.get("bold").and_then(Value::as_bool).unwrap_or(false) { &bold } else { &regular };        let leading = (pt * 0.3528 * 1.35) as f32;        for line in wrap(text, chars_per_line(pt, (w - 2.0 * margin) as f64)) {            if y - leading < margin {                let (p, l) = doc.add_page(Mm(w), Mm(h), "text");                layer = doc.get_page(p).get_layer(l);                y = h - margin;                pages += 1;            }            y -= leading;            layer.use_text(line, pt as f32, Mm(margin), Mm(y), font);        }        y -= leading * 0.5;    }    let file = std::fs::File::create(path).map_err(|e| format!("{}: {}", path, e))?;    doc.save(&mut BufWriter::new(file)).map_err(|e| e.to_string())?;    Ok(pages)}
Thread
76Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.