write_own_notes.py

Le fichier Python de l’exemple « Les notes qu’un programme se laisse ». Une épreuve d’imprimerie dont le fichier contient le tirage, la page sa plaque et la gamme de couleurs, placée deux fois, son encre. Rien n’en est dessiné et aucun logiciel de lecture ne le montre : tout cela est destiné au programme qui l’a écrit.

Python 246 lignes

À quoi sert cet exemple

Une imprimerie envoie une épreuve et la récupère trois jours plus tard, une note au crayon dans la marge. Pour reprendre le travail, il lui faut savoir à quel tirage la feuille appartient, pour quelle plaque la page a été composée, et pour quelle encre la gamme de couleurs a été tracée — et rien de tout cela ne regarde celui qui tient la feuille. Cela voyage donc sous le document plutôt que dessus : le fichier contient le tirage, la page sa plaque, et la gamme de couleurs, placée deux fois sur cette page, l’encre pour laquelle elle a été réglée.

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
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
"""Leaves a program's own notes in the file, for itself, where no reading software
shows them.

The Python twin of the `write_own_notes` example in Rust. A press room sends a proof
out and gets it back three days later with a note in the margin. To pick the job up
again it has to know which run the sheet belongs to, which plate the page was set
for, and which ink the colour bar was drawn against — and none of that is anything a
reading software should show.

So it travels under the document instead. The file carries the run, the page carries
its plate, and the drawing placed twice on it carries the ink it was set against.
Each packet is stamped with the moment its program last touched it, and each is filed
under the name of that program, so two programs leaving notes in the same file never
read each other's.

The terminal says whose the file's note is and what it reads, from the note itself.

Usage: python examples/write_own_notes.py [out.pdf]
       HQF_PDF_LANG=fr python examples/write_own_notes.py
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The margin the page is laid out inside.
MARGIN = 56.0

# How far the page runs across, between the margins.
WIDTH = 483.0

# The program the notes are filed under. Two programs leaving notes in the same file
# never read each other's, because each is filed under its own name.
PROGRAM = "PressDesk"

# When the program last touched what it left, in ISO 8601.
TOUCHED = "2026-09-07T08:15:00+02:00"

# What the file, the page and the drawing each carry.
FOR_THE_FILE = '{"run":47,"press":"K4","proof":3}'
FOR_THE_PAGE = '{"plate":"C","screen":175}'
FOR_THE_DRAWING = '{"ink":"process cyan","bar":"solid"}'

# How wide and how tall one patch of the bar is.
PATCH = (28.0, 18.0)

# How far along the bar each patch starts.
PATCH_AT = (0.0, 28.0, 56.0, 84.0, 112.0, 140.0)

# How strong the ink is in each patch.
STRENGTH = (0.15, 0.3, 0.45, 0.6, 0.8, 1.0)

# Where the bar is placed on the page, and at what scale.
PLACED = ((MARGIN, 672.0, 1.0), (MARGIN, 616.0, 0.5))


@dataclass(frozen=True)
class Words:
    """Every word the page draws, in one language."""

    # The line at the head of the page.
    title: str
    # The paragraph under it.
    body: str
    # The line under the second bar.
    caption: str


# The page in English.
ENGLISH = Words(
    title="Press proof",
    body="This sheet is a proof, and everything a reading software shows of it "
    "is on it: a title, this paragraph, and a colour bar placed twice. "
    "Under it the file states the run it belongs to, the page states the "
    "plate it was set for, and the bar states the ink it was drawn "
    "against.",
    caption="None of those three is drawn, and no reading software shows them. "
    "Each is filed under the name of the program that left it, stamped "
    "with the moment that program last touched it, and travels with "
    "the file until it opens the file again.",
)

# The page in French.
FRENCH = Words(
    title="Épreuve d’imprimerie",
    body=("Cette feuille est une épreuve, et tout ce qu’un logiciel de lecture en "
          "montre y figure : un titre, ce paragraphe et une gamme de couleurs placée "
          "deux fois. En dessous, le fichier indique le tirage auquel il appartient, "
          "la page la plaque pour laquelle elle a été composée, et la gamme l’encre "
          "par rapport à laquelle elle a été tracée."),
    caption=("Aucune des trois n’est dessinée, et aucun logiciel de lecture ne les "
             "montre. Elles sont rangées sous le nom du programme qui les a laissées, "
             "datées du moment où il y a touché pour la dernière fois, et elles "
             "voyagent avec le fichier jusqu’à ce que ce programme le rouvre."),
)

# The page in Dutch.
DUTCH = Words(
    title="Drukproef",
    body="Dit blad is een proef, en alles wat leessoftware ervan toont staat erop: "
         "een titel, deze alinea en een kleurenbalk die twee keer is geplaatst. "
         "Daaronder zegt het bestand bij welke oplage het hoort, zegt de pagina voor "
         "welke plaat ze gezet is en zegt de balk tegen welke inkt hij is afgezet.",
    caption="Geen van die drie wordt getekend, en geen leessoftware toont ze. Elk "
            "staat opgeslagen onder de naam van het programma dat het achterliet, met "
            "het moment waarop dat programma het voor het laatst aanraakte, en reist "
            "met het bestand mee tot het dat bestand weer opent.",
)

# The page in Portuguese.
PORTUGUESE = Words(
    title="Prova de impressão",
    body="Esta folha é uma prova, e tudo o que um programa de leitura mostra dela "
         "está nela: um título, este parágrafo e uma barra de cor colocada duas "
         "vezes. Por baixo, o ficheiro indica a tiragem a que pertence, a página "
         "indica a chapa para que foi composta e a barra indica a tinta com que foi "
         "comparada.",
    caption="Nenhum destes três é desenhado, e nenhum programa de leitura os mostra. "
            "Cada um é arquivado com o nome do programa que o deixou, com o momento "
            "em que esse programa lhe tocou pela última vez, e viaja com o ficheiro "
            "até que esse programa o volte a abrir.",
)

# The page in Italian.
ITALIAN = Words(
    title="Prova di stampa",
    body="Questo foglio è una prova, e tutto ciò che un programma di lettura ne "
         "mostra è su di esso: un titolo, questo paragrafo e una barra di colore "
         "collocata due volte. Sotto, il file indica la tiratura a cui appartiene, la "
         "pagina indica la lastra per cui è stata composta e la barra indica "
         "l’inchiostro con cui è stata confrontata.",
    caption="Nessuno di questi tre è disegnato, e nessun programma di lettura li "
            "mostra. Ciascuno è archiviato sotto il nome del programma che l’ha "
            "lasciato, con il momento in cui quel programma l’ha toccato l’ultima "
            "volta, e viaggia con il file finché quel programma non lo riapre.",
)

# The page in Spanish.
SPANISH = Words(
    title="Prueba de imprenta",
    body="Esta hoja es una prueba, y todo lo que un programa de lectura muestra de "
         "ella está en ella: un título, este párrafo y una barra de color colocada "
         "dos veces. Por debajo, el archivo indica la tirada a la que pertenece, la "
         "página indica la plancha para la que se compuso y la barra indica la tinta "
         "con la que se comparó.",
    caption="Ninguno de esos tres se dibuja, y ningún programa de lectura los "
            "muestra. Cada uno se archiva con el nombre del programa que lo dejó, con "
            "la fecha en que ese programa lo tocó por última vez, y viaja con el "
            "archivo hasta que vuelva a abrirlo.",
)

# The page in German.
GERMAN = Words(
    title="Andruck",
    body="Dieses Blatt ist ein Andruck, und alles, was eine Lesesoftware davon zeigt, "
         "steht darauf: ein Titel, dieser Absatz und ein zweimal platzierter "
         "Farbkontrollstreifen. Darunter nennt die Datei den Auflagenlauf, zu dem sie "
         "gehört, die Seite die Platte, für die sie eingerichtet wurde, und der "
         "Streifen die Farbe, gegen die er gezeichnet wurde.",
    caption="Keine dieser drei Angaben wird gezeichnet, und keine Lesesoftware zeigt "
            "sie. Jede ist unter dem Namen des Programms abgelegt, das sie "
            "hinterlassen hat, mit dem Zeitpunkt versehen, zu dem dieses Programm sie "
            "zuletzt berührt hat, und reist mit der Datei, bis es die Datei wieder "
            "öffnet.",
)


# Every language the example is written in. A language is added by writing its own set
# of words and naming it here.
WORDS = {_language.ENGLISH: ENGLISH, _language.FRENCH: FRENCH, _language.GERMAN: GERMAN, _language.SPANISH: SPANISH, _language.ITALIAN: ITALIAN, _language.PORTUGUESE: PORTUGUESE, _language.DUTCH: DUTCH}


def colour_bar() -> hqf_pdf.Drawing:
    """The colour bar: six patches of increasing strength, side by side."""
    bar = hqf_pdf.Content()
    for at, strength in zip(PATCH_AT, STRENGTH):
        bar.set_fill(hqf_pdf.Rgb(1.0 - strength, 1.0 - strength * 0.15, 1.0))
        bar.rect(at, 0.0, PATCH[0], PATCH[1])
        bar.fill()
    across = PATCH_AT[len(PATCH_AT) - 1] + PATCH[0]
    return hqf_pdf.Drawing(bar, hqf_pdf.Rect(0.0, 0.0, across, PATCH[1]))


def paragraph(
    content: hqf_pdf.Content,
    font: hqf_pdf.FontHandle,
    size: float,
    top: float,
    color: hqf_pdf.Rgb,
    text: str,
) -> None:
    """Writes a paragraph in a column the width of the page."""
    flow = hqf_pdf.TextFlow(font, size, leading=size * 1.4, color=color)
    lines = flow.break_lines(text, WIDTH)
    flow.draw(content, lines, MARGIN, top, WIDTH)


def main() -> None:
    language = _language.from_environment()
    words = _language.words_of(WORDS, language)

    # A named file is written as named; the default one carries the language, so the two
    # languages do not overwrite each other in `tmp/`.
    out = _out.output_path(Path(_language.file_name("own_notes.pdf", language)).stem)

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    for_the_file = hqf_pdf.PrivateData(PROGRAM, TOUCHED, FOR_THE_FILE)
    print(
        f"the file's note belongs to {for_the_file.program} and reads "
        f"{for_the_file.private}"
    )
    document.set_private_data(for_the_file)
    font = document.add_font(hqf_pdf.Font.from_path(_out.DEFAULT_FONT))

    bar = document.add_drawing(
        colour_bar().with_private_data(
            hqf_pdf.PrivateData(PROGRAM, TOUCHED, FOR_THE_DRAWING)
        )
    )

    content = hqf_pdf.Content()
    content.draw_text(font, 16.0, MARGIN, 780.0, words.title)
    paragraph(content, font, 10.0, 770.0, hqf_pdf.Rgb.gray(0.0), words.body)
    for x, y, scale in PLACED:
        content.draw_form(bar, x, y, scale)
    paragraph(content, font, 8.5, 560.0, hqf_pdf.Rgb.gray(0.35), words.caption)

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    page.set_private_data(hqf_pdf.PrivateData(PROGRAM, TOUCHED, FOR_THE_PAGE))
    document.add_page(page)

    written = document.write(out)
    print(f"wrote {out}: {written} bytes, notes on the file, the page and the drawing")


if __name__ == "__main__":
    main()