write_tiff.rs

Die Rust-Datei des Beispiels „Jedes Bild, das ein Scanner in eine Datei gelegt hat“. Eine Datei mit drei Blättern und eine Datei mit einem Foto, jedes Bild auf einer Seite in seiner eigenen Größe; das Foto kommt genau so hinein, wie die Kamera es geschrieben hat, denn die Anzeigesoftware liest ein Foto selbst.

Rust 145 Zeilen

Wofür dieses Beispiel da ist

Ein Scanner liefert nicht eine Datei pro Blatt. Er liefert eine Datei mit dem ganzen Stapel darin, und dasselbe gilt für ein Faxgerät: Jede Seite der Übertragung ist darin, eine nach der anderen. Software, die nur die erste ansieht, verliert stillschweigend den Rest, und niemand merkt es bis zu dem Tag, an dem jemand Seite vier braucht.

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
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Places every picture a TIFF holds on a page of its own, and writes the
//! result to disk.
//!
//! A TIFF holds any number of pictures, one directory apiece: a scanner puts
//! every sheet of a batch in one file, and a fax puts every page of a call
//! there. Each of them gets a page exactly its own size — at 72 dots to the
//! inch one pixel is one point — so the page that comes back out of a renderer
//! is the picture that went in.
//!
//! Named nothing, the example reads the two committed fixtures: one holding a
//! photograph in a single strip, whose bytes go into the document undecoded
//! because PDF reads a photograph itself, and one holding three pictures whose
//! samples are read and written again. It opens on a sheet laying all of them
//! out the way a document would.
//!
//! Usage: `cargo run --example write_tiff -- tmp/tiff.pdf [file.tif...]`

use std::env;
use std::fs;
use std::path::{Path, PathBuf};

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

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

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

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

/// How wide the sheet the showcase is laid out on is.
const A4_WIDTH: f64 = 595.276;

/// The side of the box each picture of the showcase is fitted into, and how far
/// apart the boxes stand.
const BOX_SIDE: f64 = 120.0;

/// See [`BOX_SIDE`].
const BOX_STEP: f64 = 130.0;

/// The files the example reads when the caller names none: the ones committed
/// for the tests, so that it runs on any machine.
fn default_files() -> Vec<PathBuf> {
    let images = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("images");
    vec![images.join("photograph.tif"), images.join("pages.tif")]
}

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

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

    let paths: Vec<PathBuf> = args.map(PathBuf::from).collect();
    let showcase = paths.is_empty();
    let paths = if showcase { default_files() } else { paths };

    let mut doc = Document::new();
    doc.set_license(licence::licensed());
    doc.set_info(Name::new("Title"), "hqf-pdf TIFF sample");

    let mut handles = Vec::new();
    for path in &paths {
        let bytes = fs::read(path)?;
        let held = Image::tiff_pages(&bytes)?;
        println!("{}: {held} picture(s)", path.display());

        for page in 0..held {
            let image = Image::from_tiff_page(&bytes, page)?;
            println!(
                "  {}: {}x{} {:?}, {} bits a component, {}",
                page + 1,
                image.width(),
                image.height(),
                image.color_space(),
                image.bits_per_component(),
                image.resolution().map_or_else(
                    || "stating no resolution of its own".to_owned(),
                    |resolution| {
                        format!("{} by {} dots to the inch", resolution.x(), resolution.y())
                    },
                )
            );
            handles.push(doc.add_image(image));
        }
    }

    if showcase {
        doc.add_page(sheet_of_all(&handles)?)?;
    }
    for handle in &handles {
        // A page the size of the picture, with the picture filling it. The size
        // is the one the file itself asks for; a file asking for none is laid
        // down one pixel to the point.
        let (width, height) = handle.size();
        let mut c = Content::new();
        c.draw_image(handle, 0.0, 0.0, width, height)?;

        let mut page = Page::new(width, height);
        page.content = c.into_bytes();
        doc.add_page(page)?;
    }

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

    println!(
        "wrote {out}: {} bytes, {} page(s)",
        bytes.len(),
        doc.page_count()
    );
    Ok(())
}

/// One sheet holding every picture the files gave, each fitted whole into a box
/// of one size and standing on the same line as the rest.
fn sheet_of_all(handles: &[ImageHandle]) -> Result<Page, Box<dyn std::error::Error>> {
    let mut c = Content::new();

    c.set_fill(Rgb::new(0.93, 0.94, 0.96))?;
    c.rect(0.0, 700.0, A4_WIDTH, 142.0)?;
    c.fill();

    let mut left = 60.0;
    for handle in handles {
        let (width, height) = handle.fit_within(BOX_SIDE, BOX_SIDE)?;
        c.draw_image(handle, left, 600.0, width, height)?;
        left += BOX_STEP;
    }

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