write_pdf.rs

Le fichier Rust de l'exemple « Un PDF minimal ». Deux pages identiques qui portent l'ossature d'une facture en formes simples, et pas un mot de texte : un fichier d'une douzaine de kilooctets, sans aucune police incorporée.

Rust 87 lignes

À quoi sert cet exemple

Ouvrez celui-ci en premier. Deux pages identiques, chacune avec le squelette d'une facture — une bande de couleur en haut, un cadre autour de tout, et deux douzaines de rectangles vides là où iraient les lignes d'un tableau — et pas un mot de texte nulle part sur l'une ou l'autre. C'est tout ce qu'il faut à un document pour exister : des pages, et des marques dessus. Il y en a deux plutôt qu'une parce que mettre la seconde coûte une seule instruction, et cela vaut d'être vu dès le départ. Tout le reste de ce site est bâti là-dessus, jamais à la place.

Ce que montre cet exemple

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! Writes a small PDF to disk, so that real reading software can be pointed at
//! it.
//!
//! Unit tests can only check that the bytes look right to us. Whether the file
//! is actually a PDF is a question only reading software can answer, which is
//! what `scripts/check_pdf_validity.sh` uses this for.
//!
//! Usage: `cargo run --example write_pdf -- tmp/out.pdf [--licensed]`

use std::env;
use std::fs;
use std::path::Path;

use hqf_pdf::content::Content;
use hqf_pdf::cos::Name;
use hqf_pdf::{Document, Page};

#[path = "shared/out.rs"]
mod out;

#[path = "shared/licence.rs"]
mod licence;

#[path = "shared/failure.rs"]
mod failure;

/// Draws a page of an invoice: a header block, a frame, and a run of table
/// rules.
fn invoice_page() -> Result<Page, hqf_pdf::Error> {
    let mut c = Content::new();
    c.save_state();

    c.set_fill_rgb(0.20, 0.40, 0.80)?;
    c.rect(50.0, 700.0, 495.0, 90.0)?;
    c.fill();

    c.set_stroke_rgb(0.0, 0.0, 0.0)?;
    c.set_line_width(1.0)?;
    c.rect(50.0, 50.0, 495.0, 742.0)?;
    c.stroke();

    c.set_line_width(0.4)?;
    for row in 0..24 {
        let y = f64::from(row).mul_add(-22.0, 640.0);
        c.rect(50.0, y, 495.0, 22.0)?;
        c.stroke();
    }
    c.restore_state();

    let mut page = Page::a4();
    page.content = c.into_bytes();
    Ok(page)
}

fn main() -> std::process::ExitCode {
    failure::reported(run())
}

fn run() -> Result<(), Box<dyn std::error::Error>> {
    let mut args = env::args().skip(1);
    let path = args.next().unwrap_or_else(|| out::default_path("out"));
    let licensed = env::args().any(|a| a == "--licensed");

    let mut doc = Document::new();
    if licensed {
        doc.set_license(licence::licensed());
    }
    doc.set_info(Name::new("Title"), "hqf-pdf sample invoice");
    doc.set_info(Name::new("Creator"), "hqf-pdf");

    doc.add_page(invoice_page()?)?;
    doc.add_page(invoice_page()?)?;

    let bytes = doc.to_bytes()?;
    if let Some(parent) = Path::new(&path).parent() {
        fs::create_dir_all(parent)?;
    }
    fs::write(&path, &bytes)?;

    println!(
        "wrote {path}: {} bytes, {} pages, {}",
        bytes.len(),
        doc.page_count(),
        if licensed { "licensed" } else { "evaluation" }
    );
    Ok(())
}