write_pdf.rs

Die Rust-Datei des Beispiels „Ein minimales PDF“. Zwei identische Seiten, die die Struktur einer Rechnung mit Linien und Rechtecken zeichnen, ohne ein Wort Text: Die Datei hat etwa ein Dutzend Kilobyte und bettet keine Schrift ein.

Rust 87 Zeilen

Wofür dieses Beispiel da ist

Öffnen Sie dieses zuerst. Zwei gleiche Seiten, jede mit dem Gerüst einer Rechnung — ein farbiges Band oben, ein Rahmen um alles und zwei Dutzend leere Rechtecke, wo die Zeilen einer Tabelle hinkämen — und auf keiner ein Wort Text. Mehr braucht ein Dokument nicht, um zu existieren: Seiten und Markierungen darauf. Es sind zwei statt einer, weil das Hinzufügen der zweiten eine einzige Anweisung kostet, und das lohnt sich gleich zu Beginn zu sehen. Alles andere auf dieser Website baut darauf auf, nie an seiner Stelle.

Was dieses Beispiel zeigt

 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(())
}