write_tiff.py

Le fichier Python de l'exemple « Toutes les images qu'un scanner met dans un seul fichier ». Un fichier de trois feuilles et un fichier d'une seule photographie, chaque image sur une page taillée à sa mesure ; la photographie entre telle que l'appareil l'a écrite, puisqu'un logiciel de lecture sait déjà lire une photographie.

Python 116 lignes

À quoi sert cet exemple

Un scanner ne vous rend pas un fichier par feuille. Il en rend un seul, avec toute la pile dedans, et un télécopieur fait pareil : toutes les pages de l'appel sont là, l'une après l'autre. Un logiciel qui ne regarde que la première perd les autres sans rien dire, et personne ne s'en aperçoit avant le jour où il faut la page quatre.

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