Un dessin, placé autant de fois qu'on veut

Un badge construit une seule fois comme dessin, puis posé huit fois, plus une fois à moitié taille : le fichier ne garde le dessin qu'une seule fois, quel que soit le nombre de poses.

Python write_drawing.py 192 lignes
  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
"""Draws one badge, and places it eight times.

The Python twin of the `write_drawing` example in Rust. The badge — its frame, its
coloured head, its mark — is built once as a drawing, and the file holds its
operators once however many times it is placed. Each placing costs a name and a
matrix. The names on the badges are drawn by the page, over the drawing, because
they are what differs.

What the page reads is held in `Words`, once per language, and `HQF_PDF_LANG` picks
which set is drawn. The guests' names are not language, and neither is the company's:
a name is written the way its owner writes it.

Usage: python examples/write_drawing.py [out.pdf] [font.ttf]
       HQF_PDF_LANG=fr python examples/write_drawing.py
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The size of one badge, in points.
BADGE = (220.0, 96.0)

# The left edge of everything the page draws.
LEFT = 70.0

# Who the badges are for. A name is not language.
GUESTS = [
    "Amélie Roussel",
    "Bertrand Colin",
    "Chloé Marchand",
    "Damien Leclerc",
    "Élise Fontaine",
    "Fabien Deschamps",
    "Gabrielle Vidal",
    "Hugo Mercier",
]

# The company the badges are issued by, drawn in the badge itself. A company writes its
# name one way.
COMPANY = "HQF Development"


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

    # The line at the head of the page.
    title: str
    # The line under it, saying what a drawing costs.
    intro: str
    # What the badge itself says under the company's name, drawn once for all eight.
    occasion: str
    # What each guest does, in the order the badges are placed.
    roles: tuple[str, str, str, str, str, str, str, str]
    # The line under the badge placed at half size.
    half: str


# The page in English.
ENGLISH = Words(
    title="One badge, placed eight times",
    intro=(
        "The badge is drawn once; the file holds its operators once, whatever it "
        "costs to place."
    ),
    occasion="Open house — visitor",
    roles=(
        "Bindery",
        "Typesetting",
        "Prepress",
        "Colour",
        "Plates",
        "Press",
        "Finishing",
        "Despatch",
    ),
    half="The same drawing, placed at half size.",
)

# The page in French.
FRENCH = Words(
    title="Un badge, posé huit fois",
    intro=(
        "Dessiné une fois, le badge n'a qu'un jeu d'instructions dans le fichier, "
        "où qu'il soit posé."
    ),
    occasion="Portes ouvertes — visiteur",
    roles=(
        "Reliure",
        "Composition",
        "Prépresse",
        "Couleur",
        "Plaques",
        "Impression",
        "Façonnage",
        "Expédition",
    ),
    half="Le même dessin, posé à la moitié de sa taille.",
)


# 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}


def badge(font: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Drawing:
    """The badge itself: everything every badge has, drawn from the origin."""
    width, height = BADGE
    content = hqf_pdf.Content()

    content.set_fill(hqf_pdf.Rgb(0.96, 0.97, 0.99))
    content.rect(0.0, 0.0, width, height)
    content.fill()

    content.set_fill(hqf_pdf.Rgb(0.12, 0.28, 0.52))
    content.rect(0.0, height - 24.0, width, 24.0)
    content.fill()

    content.set_stroke(hqf_pdf.Rgb(0.12, 0.28, 0.52))
    content.set_line_width(1.0)
    content.rect(0.0, 0.0, width, height)
    content.stroke()

    content.set_fill(hqf_pdf.Rgb(1.0, 1.0, 1.0))
    content.draw_text(font, 11.0, 12.0, height - 17.0, COMPANY)

    content.set_fill(hqf_pdf.Rgb.gray(0.45))
    content.draw_text(font, 8.0, 12.0, 12.0, words.occasion)

    return hqf_pdf.Drawing(content, hqf_pdf.Rect(0.0, 0.0, width, height))


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("drawing.pdf", language)).stem)

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    font = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))
    handle = document.add_drawing(badge(font, words))

    width, height = BADGE
    content = hqf_pdf.Content()
    content.draw_text(font, 15.0, LEFT, 780.0, words.title)
    content.save_state()
    content.set_fill(hqf_pdf.Rgb.gray(0.35))
    content.draw_text(font, 10.0, LEFT, 762.0, words.intro)
    content.restore_state()

    y = 600.0
    for start in range(0, len(GUESTS), 2):
        x = LEFT
        for index in range(start, min(start + 2, len(GUESTS))):
            content.draw_form(handle, x, y, 1.0)
            content.draw_text(font, 14.0, x + 12.0, y + 48.0, GUESTS[index])
            content.save_state()
            content.set_fill(hqf_pdf.Rgb.gray(0.4))
            content.draw_text(font, 10.0, x + 12.0, y + 32.0, words.roles[index])
            content.restore_state()
            x += width + 20.0
        y -= height + 20.0

    # The same drawing again, at half size: a placing carries its own scale.
    content.draw_form(handle, LEFT, 150.0, 0.5)
    content.save_state()
    content.set_fill(hqf_pdf.Rgb.gray(0.35))
    content.draw_text(font, 10.0, LEFT, 134.0, words.half)
    content.restore_state()

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    document.add_page(page)

    written = document.write(out)
    print(f"wrote {out}: {written} bytes, {len(GUESTS) + 1} placings of one drawing")


if __name__ == "__main__":
    main()