write_tiff.py

Die Python-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.

Python 116 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
"""Places every picture a TIFF holds on a page of its own, and writes the result to
disk.

The Python twin of the `write_tiff` example in Rust.

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: python examples/write_tiff.py [out.pdf] [file.tif...]
"""

from __future__ import annotations

import sys
from pathlib import Path

import _licence
import _out

import hqf_pdf

# The files the example reads when the caller names none: the ones committed for the
# tests, so that it runs on any machine.
IMAGES = Path(__file__).resolve().parents[2] / "hqf-pdf" / "tests" / "images"
DEFAULT_FILES = [IMAGES / "photograph.tif", IMAGES / "pages.tif"]

# How wide the sheet the showcase is laid out on is.
A4_WIDTH = 595.276

# The side of the box each picture of the showcase is fitted into, and how far apart the
# boxes stand.
BOX_SIDE = 120.0
BOX_STEP = 130.0


def sheet_of_all(handles: list) -> hqf_pdf.Page:
    """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."""
    content = hqf_pdf.Content()

    content.set_fill(hqf_pdf.Rgb(0.93, 0.94, 0.96))
    content.rect(0.0, 700.0, A4_WIDTH, 142.0)
    content.fill()

    left = 60.0
    for handle in handles:
        width, height = handle.fit_within(BOX_SIDE, BOX_SIDE)
        content.draw_image(handle, left, 600.0, width, height)
        left += BOX_STEP

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    return page


def main() -> None:
    out = _out.output_path("tiff")

    named = [Path(argument) for argument in sys.argv[2:]]
    showcase = not named
    paths = DEFAULT_FILES if showcase else named

    doc = hqf_pdf.Document()
    doc.set_license(_licence.licensed())
    doc.set_info("Title", "hqf-pdf TIFF sample")

    handles = []
    for path in paths:
        data = path.read_bytes()
        held = hqf_pdf.Image.tiff_pages(data)
        print(f"{path}: {held} picture(s)")

        for page in range(held):
            image = hqf_pdf.Image.from_tiff_page(data, page)
            resolution = image.resolution
            declared = (
                f"{resolution.x} by {resolution.y} dots to the inch"
                if resolution is not None
                else "stating no resolution of its own"
            )
            print(
                f"  {page + 1}: {image.width}x{image.height} {image.color_space!r},"
                f" {image.bits_per_component} bits a component, {declared}"
            )
            handles.append(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.
        width, height = handle.size
        content = hqf_pdf.Content()
        content.draw_image(handle, 0.0, 0.0, width, height)

        sheet = hqf_pdf.Page(width, height)
        sheet.set_content(content)
        doc.add_page(sheet)

    pdf = doc.to_bytes()
    Path(out).parent.mkdir(parents=True, exist_ok=True)
    Path(out).write_bytes(pdf)

    print(f"wrote {out}: {len(pdf)} bytes, {doc.page_count} page(s)")


if __name__ == "__main__":
    main()