Marge, bordure, marge intérieure et fond d'une cellule

Ce qu'une cellule fait de sa propre boîte : la marge, la bordure, la marge intérieure, le fond.

Python write_table_borders.py 223 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
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
"""Shows what a cell can do with its own box: the margin around it, the border on
each of its sides, and the padding within it.

The Python twin of the `write_table_borders` example in Rust. The table declares no
rule of its own, so every line on the page is drawn by the cell it belongs to. That
is the whole point of a cell border: it is the cell's, not the grid's, and a cell
can carry one on a single side.

Every word the sheet draws is held in `Words`, once per language, and `HQF_PDF_LANG`
picks which one is drawn.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The margins, and the box the table is fitted into.
MARGIN = 56.0
TOP = 780.0
PAGE_WIDTH = 595.276
TABLE_WIDTH = PAGE_WIDTH - 2 * MARGIN


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

    # The line set over the sheet.
    title: str
    # The word drawn inside every demonstration cell.
    cell: str
    # What each sample demonstrates, in the order ``samples`` builds them.
    samples: tuple[str, ...]


# The sheet in English.
ENGLISH = Words(
    title="Borders, margins and padding",
    cell="Cell",
    samples=(
        "The top, and nothing else",
        "The bottom, and nothing else",
        "The left",
        "The right",
        "The top and the bottom",
        "All four sides",
        "A 0.25 pt rule",
        "A 2.5 pt rule",
        "A weight of its own on each side",
        "A rule in colour",
        "A margin holds the cell off its column",
        "A negative margin runs it past its column instead",
        "Padding holds the text off the rule",
    ),
)

# The sheet in French.
FRENCH = Words(
    title="Bordures, marges et retraits",
    cell="Cellule",
    samples=(
        "Le haut, et rien d'autre",
        "Le bas, et rien d'autre",
        "La gauche",
        "La droite",
        "Le haut et le bas",
        "Les quatre côtés",
        "Un filet de 0.25 pt",
        "Un filet de 2.5 pt",
        "Une épaisseur par côté",
        "Un filet en couleur",
        "Une marge écarte la cellule de sa colonne",
        "Une marge négative la fait déborder de sa colonne",
        "Un retrait écarte le texte du filet",
    ),
)


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


@dataclass(frozen=True)
class Sample:
    """One line of the sheet: the cell that shows a sample, and how it is built."""

    border: hqf_pdf.Border
    margin: hqf_pdf.Margin
    padding: hqf_pdf.Padding
    fill: hqf_pdf.Rgb | None = None


def samples() -> list[Sample]:
    """The samples, in the order ``Words.samples`` names them."""
    rule = hqf_pdf.Stroke(0.8)
    hairline = hqf_pdf.Stroke(0.25)
    heavy = hqf_pdf.Stroke(2.5)
    ink = hqf_pdf.Stroke(1.0, hqf_pdf.Rgb(0.78, 0.14, 0.12))

    def plain(border: hqf_pdf.Border) -> Sample:
        return Sample(
            border,
            hqf_pdf.Margin.symmetric(0.0, 3.0),
            hqf_pdf.Padding.symmetric(6.0, 4.0),
        )

    shade = hqf_pdf.Rgb.gray(0.92)
    pad = hqf_pdf.Padding.symmetric(6.0, 4.0)

    return [
        plain(hqf_pdf.Border(top=rule)),
        plain(hqf_pdf.Border(bottom=rule)),
        plain(hqf_pdf.Border(left=rule)),
        plain(hqf_pdf.Border(right=rule)),
        plain(hqf_pdf.Border(top=rule, bottom=rule)),
        plain(hqf_pdf.Border.uniform(rule)),
        plain(hqf_pdf.Border.uniform(hairline)),
        plain(hqf_pdf.Border.uniform(heavy)),
        plain(hqf_pdf.Border(top=hairline, bottom=heavy, left=rule, right=rule)),
        plain(hqf_pdf.Border.uniform(ink)),
        Sample(
            hqf_pdf.Border.uniform(rule),
            hqf_pdf.Margin.uniform(10.0),
            pad,
            shade,
        ),
        Sample(
            hqf_pdf.Border.uniform(rule),
            hqf_pdf.Margin.symmetric(-10.0, 3.0),
            pad,
            shade,
        ),
        Sample(
            hqf_pdf.Border.uniform(rule),
            hqf_pdf.Margin.symmetric(0.0, 3.0),
            hqf_pdf.Padding.uniform(14.0),
            shade,
        ),
    ]


def border_sheet(font: hqf_pdf.FontHandle, words: Words) -> hqf_pdf.Table:
    """The sheet: a line per sample, its name on the left and the cell that shows
    it on the right."""
    # No rule is declared on this table: every line the page shows is a cell's own
    # border.
    columns = hqf_pdf.Columns(
        [hqf_pdf.ColumnWidth.fraction(1.0), hqf_pdf.ColumnWidth.points(210.0)],
        TABLE_WIDTH,
    )
    table = hqf_pdf.Table(columns)

    for sample, label in zip(samples(), words.samples):
        table.push(
            hqf_pdf.Row(
                [
                    hqf_pdf.Cell(
                        font,
                        9.0,
                        label,
                        valign=hqf_pdf.VAlign.Middle,
                        padding=hqf_pdf.Padding.symmetric(0.0, 4.0),
                    ),
                    hqf_pdf.Cell(
                        font,
                        9.0,
                        words.cell,
                        align=hqf_pdf.Align.Center,
                        valign=hqf_pdf.VAlign.Middle,
                        border=sample.border,
                        margin=sample.margin,
                        padding=sample.padding,
                        fill=sample.fill,
                    ),
                ],
                min_height=34.0,
            )
        )

    return table


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

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())
    font = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))

    table = border_sheet(font, words)

    content = hqf_pdf.Content()
    content.draw_text(font, 14.0, MARGIN, TOP + 24.0, words.title)

    placed = table.fit(MARGIN, TOP, TOP - MARGIN)
    placed.draw(content)

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

    written = document.write(out)
    print(f"wrote {out}: {written} bytes, 1 page, {table.row_count} rows")


if __name__ == "__main__":
    main()