"""A sheet as it goes to the press, and what each box a page states does to it.

The Python twin of the `write_press_sheet` example in Rust. A page has always had
a sheet. It now also has the four boxes a printer reads: what the ink runs to,
where the blade falls, what is worth keeping, and what reading software shows.

What the sheet says is written in the language `HQF_PDF_LANG` names. The prices are
not, and neither are the measurements: the two the note quotes are read off the sheet
the page is drawn on.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# How far past the blade the ink runs.
BLEED_OVER = 9.0
# How much sheet is left outside the ink for the marks.
MARK_ROOM = 27.0
# How long one arm of a corner mark is.
MARK_LENGTH = 18.0

# How much sheet stands outside the finished page on every side.
MARGIN = BLEED_OVER + MARK_ROOM

# The finished page: where the blade falls, in the sheet's own coordinates.
TRIM = (MARGIN, MARGIN, 400.0, 560.0)

# The whole sheet: the finished page, the ink that runs past it, and the room the marks
# stand in.
SHEET = (TRIM[2] + MARGIN + MARGIN, TRIM[3] + MARGIN + MARGIN)

# Where the lower-left corner of the fourth sheet sits: that sheet is the right half of
# a press sheet two pages wide, so its corner stands where the first sheet ends.
MOVED = (SHEET[0], 0.0)

# How far inside the blade the drawing itself keeps.
ART_INSET = 40.0

# The colour the band across the top is laid in.
BAND = (0.11, 0.26, 0.44)

# A4, in points.
A4 = (595.276, 841.890)

# The company whose list is being printed, which no language renames.
COMPANY = "HQF Development"

# The sizes the sheet is set in, in points.
TITLE_SIZE = 22.0
BODY_SIZE = 10.0
FOOT_SIZE = 8.0
NOTE_SIZE = 7.0
CAPTION_SIZE = 8.0
TURNED_TITLE_SIZE = 18.0

# How far one line of body stands under the one above it, in points.
BODY_STEP = 16.0

# Where the label over the arrow between the two sketches begins.
ARROW_LABEL_LEFT = 288.0

# What each row of the list costs, in the order the rows are drawn. A price is not
# language.
PRICES = ("18.40", "6.90", "42.00", "31.50", "12.75", "9.20")

# How far in from the right of what is worth keeping a price stands.
PRICE_INSET = 30.0

# How far in from the right of its box a guide's name stands.
GUIDE_INSET = 30.0

# Where the lowest line of the note in the margin sits, and how far the one above it
# stands.
NOTE_BASELINE = 8.0
NOTE_STEP = 9.0

# The two sketches: where each stands on the turned page.
SKETCHES = ((150.0, 140.0, 110.0, 155.0), (400.0, 165.0, 155.0, 110.0))

# Where the turned page sets every line from.
TURNED_LEFT = 70.0


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

    The prices are not among them, nor the two measurements the last page quotes: those
    are read off the sheet.
    """

    # What the list being printed is called, in the band at the head.
    title: str
    # The line under it, which says what the band does at the blade.
    band: str
    # The paragraph that says why a sheet allows for the blade twice over.
    blade: tuple[str, ...]
    # The paragraph that says what the boxes spare the reader.
    boxes: tuple[str, ...]
    # What each row of the list is, in the order they are drawn.
    rows: tuple[str, ...]
    # What stands after the name of the company at the foot.
    foot: str
    # What names each guide, in the order they are drawn: the ink, the blade, then what
    # is worth keeping.
    guides: tuple[str, ...]
    # The note in the margin of the first sheet, which shows whole.
    first_note: tuple[str, ...]
    # The note in the margin of the second, which is cropped to the blade.
    second_note: tuple[str, ...]
    # The note in the margin of the fourth, whose sheet is moved off the origin.
    moved_note: tuple[str, ...]
    # What the turned page is called.
    turned_title: str
    # What stands before the width of the sheet in the first line of the turned page.
    across_before: str
    # What stands between the width and the height.
    across_between: str
    # What stands after the height.
    across_after: str
    # The rest of that first paragraph, which says what the page adds.
    quarter: str
    # The paragraph that says what reading software does with the turn.
    reading_software: tuple[str, ...]
    # What the sheet drawn as the file holds it is called.
    in_file: str
    # What the sheet drawn as reading software shows it is called.
    on_screen: str
    # What the arrow between the two sketches says.
    turn: str


# The sheet in English.
ENGLISH = Words(
    title="Autumn list",
    band="The band above runs off the blade: no white edge is left.",
    blade=(
        "The blade never falls exactly where it is",
        "aimed. A sheet allows for that twice over:",
        "the ink runs past the cut, and nothing that",
        "matters stands within a few points of it.",
    ),
    boxes=(
        "The boxes say all three distances, so nobody",
        "has to measure the file to find out where the",
        "page really ends.",
    ),
    rows=(
        "Linen paper, ream",
        "Bookbinder's thread",
        "Corner mark punch",
        "Trim gauge, steel",
        "Bleed guide, acrylic",
        "Press blanket wash",
    ),
    foot=" — one sheet, four boxes",
    guides=("bleed", "trim", "art"),
    first_note=(
        "Page 1 states the bleed, the trim and the art, and no crop: reading software "
        "shows the whole",
        "sheet, marks and all, which is what the press wants to see.",
    ),
    second_note=(
        "Page 2 is the same drawing and the same boxes, plus a crop set to the trim: "
        "reading software",
        "shows the finished page alone. Nothing was removed — what is "
        "outside is still here.",
    ),
    moved_note=(
        "Page 4 is the right half of a press sheet two pages wide: its lower-left "
        "corner stands where",
        "page 1 ends, and its drawing and boxes are stated from there. It shows just "
        "as page 1 does.",
    ),
    turned_title="A landscape page inside an upright document",
    across_before="This page is the same upright sheet as the two before it: ",
    across_between=" points across, ",
    across_after=" up. What",
    quarter="it adds is one number saying that reading software turns it a quarter "
    "clockwise before showing it.",
    reading_software=(
        "So the words are wider than the sheet they are written on, and nobody "
        "has to be handed a",
        "second page size to say so. Reading software printing the file turns the "
        "paper rather than the",
        "drawing, and reading software showing it hands you the page the way round it "
        "was meant.",
    ),
    in_file="as it sits in the file",
    on_screen="as reading software shows it",
    turn="a quarter clockwise",
)

# The sheet in French.
FRENCH = Words(
    title="Catalogue d’automne",
    band="Le bandeau déborde la coupe : aucun liseré blanc ne reste.",
    blade=(
        "La lame ne tombe jamais exactement là où l’on",
        "la vise. La feuille s’en prémunit deux fois :",
        "l’encre déborde la coupe, et rien de ce qui",
        "compte n’est à moins de quelques points.",
    ),
    boxes=(
        "Les boîtes donnent les trois distances, donc",
        "personne n’a à mesurer le fichier pour savoir",
        "où la page finit vraiment.",
    ),
    rows=(
        "Papier de lin, rame",
        "Fil de relieur",
        "Emporte-pièce à repères",
        "Calibre de coupe, acier",
        "Guide de fond perdu, acrylique",
        "Lavant pour blanchet",
    ),
    foot=" — une feuille, quatre boîtes",
    guides=("fond perdu", "coupe", "zone utile"),
    first_note=(
        ("La page 1 donne le fond perdu, la coupe et la zone utile, sans recadrage : "
         "le logiciel de lecture montre"),
        "toute la feuille, repères compris, ce que l’imprimeur veut voir.",
    ),
    second_note=(
        ("La page 2 reprend le même contenu et les mêmes boîtes, plus un recadrage sur"
         " la coupe : le logiciel de lecture"),
        ("montre la page finie seule. Rien n’a été retiré : ce qui dépasse est "
         "toujours là."),
    ),
    moved_note=(
        ("La page 4 est la moitié droite d’une feuille large de deux pages : son coin "
         "inférieur gauche est là où"),
        ("finit la page 1, et son contenu et ses boîtes sont repérés depuis ce point. "
         "Elle s’affiche comme la page 1."),
    ),
    turned_title="Une page à l’italienne dans un document à la française",
    across_before=("Cette page est la même feuille à la française que les deux "
                   "précédentes : "),
    across_between=" points de large, ",
    across_after=" de haut. Ce",
    quarter=("qu’elle ajoute est un nombre : le logiciel de lecture la tourne d’un "
             "quart de tour horaire avant de l’afficher."),
    reading_software=(
        ("Les mots sont donc plus larges que la feuille sur laquelle ils sont écrits, "
         "et personne n’a"),
        ("besoin d’un second format de page pour le dire. Un logiciel de lecture qui "
         "imprime le fichier tourne le"),
        ("papier plutôt que le contenu, et un logiciel de lecture qui l’affiche vous "
         "présente la page dans le bon sens."),
    ),
    in_file="telle qu’elle est dans le fichier",
    on_screen="telle qu’un logiciel de lecture la montre",
    turn="un quart de tour horaire",
)

# The sheet in Dutch.
DUTCH = Words(
    title="Najaarsaanbieding",
    band="De band loopt voorbij het mes: er blijft geen witte rand over.",
    blade=(
        "Het mes valt nooit precies waar het op",
        "gericht is. Een blad houdt daar twee keer rekening mee:",
        "de inkt loopt voorbij de snede, en niets wat",
        "ertoe doet staat binnen een paar punten ervan.",
    ),
    boxes=(
        "De kaders noemen alle drie de afstanden, dus niemand",
        "hoeft het bestand op te meten om te weten waar de",
        "pagina echt eindigt.",
    ),
    rows=(
        "Linnenpapier, riem",
        "Boekbindersgaren",
        "Hoekstans",
        "Snijmal, staal",
        "Aflooprichtlijn, acryl",
        "Rubberdoekreiniger",
    ),
    foot=" — één blad, vier kaders",
    guides=("afloop", "snijlijn", "beeld"),
    first_note=(
        "Pagina 1 noemt de afloop, de snijlijn en het beeld, en geen uitsnede: "
        "leessoftware toont het hele",
        "blad, met tekens en al, wat de pers wil zien.",
    ),
    second_note=(
        "Pagina 2 is dezelfde tekening met dezelfde kaders, plus een uitsnede op de "
        "snijlijn: leessoftware",
        "toont alleen de afgewerkte pagina. Niets is weggehaald: wat erbuiten staat, "
        "is er nog.",
    ),
    moved_note=(
        "Pagina 4 is de rechterhelft van een drukvel van twee pagina’s breed: de "
        "linkeronderhoek staat waar",
        "pagina 1 eindigt, en de tekening en kaders worden van daaruit vermeld. Ze "
        "wordt getoond zoals pagina 1.",
    ),
    turned_title="Een liggende pagina in een staand document",
    across_before="Deze pagina is hetzelfde staande blad als de twee ervoor:",
    across_between=" punten breed,",
    across_after=" hoog. Wat ze",
    quarter="toevoegt, is één getal dat zegt dat leessoftware haar een kwartslag met "
            "de klok mee draait voordat ze haar toont.",
    reading_software=(
        "Zo zijn de woorden breder dan het blad waarop ze staan, en hoeft niemand een "
        "tweede",
        "paginaformaat te krijgen om dat te zeggen. Leessoftware die het bestand "
        "afdrukt, draait het papier in plaats van de",
        "tekening, en leessoftware die het toont, geeft u de pagina zoals ze bedoeld "
        "is.",
    ),
    in_file="zoals ze in het bestand staat",
    on_screen="zoals leessoftware haar toont",
    turn="een kwartslag met de klok mee",
)

# The sheet in Portuguese.
PORTUGUESE = Words(
    title="Catálogo de outono",
    band="A faixa acima sai para lá da lâmina: não fica margem branca.",
    blade=(
        "A lâmina nunca cai exatamente onde se",
        "aponta. Uma folha prevê isso duas vezes:",
        "a tinta passa do corte, e nada que",
        "importe fica a poucos pontos dele.",
    ),
    boxes=(
        "As caixas dizem as três distâncias, por isso ninguém",
        "tem de medir o ficheiro para saber onde",
        "acaba de facto a página.",
    ),
    rows=(
        "Papel de linho, resma",
        "Linha de encadernador",
        "Furador de cantos",
        "Calibre de corte, aço",
        "Guia de sangria, acrílico",
        "Lavagem da borracha",
    ),
    foot=" — uma folha, quatro caixas",
    guides=("sangria", "corte", "arte"),
    first_note=(
        "A página 1 indica a sangria, o corte e a arte, e nenhum recorte: o programa "
        "de leitura mostra a folha",
        "toda, marcas incluídas, que é o que a gráfica quer ver.",
    ),
    second_note=(
        "A página 2 é o mesmo desenho com as mesmas caixas e um recorte ajustado ao "
        "corte: o programa de leitura",
        "mostra só a página acabada. Nada foi retirado: o que está fora continua aqui.",
    ),
    moved_note=(
        "A página 4 é a metade direita de uma folha com duas páginas de largura: o "
        "canto inferior esquerdo fica onde",
        "acaba a página 1, e o seu desenho e as suas caixas são indicados a partir "
        "daí. Vê-se tal como a página 1.",
    ),
    turned_title="Uma página ao baixo dentro de um documento ao alto",
    across_before="Esta página é a mesma folha ao alto que as duas anteriores:",
    across_between=" pontos de largura,",
    across_after=" de altura. O que",
    quarter="acrescenta é um número: o programa de leitura roda-a um quarto de volta "
            "no sentido dos ponteiros do relógio antes de a mostrar.",
    reading_software=(
        "Assim as palavras são mais largas do que a folha em que estão escritas, e "
        "ninguém precisa de receber um",
        "segundo tamanho de página para o dizer. Ao imprimir o ficheiro, o programa "
        "de leitura roda o papel e não o",
        "desenho, e ao mostrá-lo dá-lhe a página no sentido em que foi pensada.",
    ),
    in_file="tal como está no ficheiro",
    on_screen="tal como o programa de leitura a mostra",
    turn="um quarto de volta no sentido dos ponteiros do relógio",
)

# The sheet in Italian.
ITALIAN = Words(
    title="Catalogo d’autunno",
    band="La fascia in alto supera il taglio: niente bordo bianco.",
    blade=(
        "La lama non cade mai esattamente dove è",
        "puntata. Un foglio ne tiene conto due volte:",
        "l’inchiostro passa oltre il taglio, e niente di",
        "importante sta a pochi punti da esso.",
    ),
    boxes=(
        "I riquadri dicono le tre distanze, così nessuno",
        "deve misurare il file per scoprire dove",
        "finisce davvero la pagina.",
    ),
    rows=(
        "Carta di lino, risma",
        "Filo da legatoria",
        "Fustella per angoli",
        "Calibro di taglio, acciaio",
        "Guida di abbondanza, acrilico",
        "Detergente per caucciù",
    ),
    foot=" — un foglio, quattro riquadri",
    guides=("abbondanza", "rifilo", "grafica"),
    first_note=(
        "La pagina 1 indica l’abbondanza, il rifilo e la grafica, e nessun ritaglio: "
        "il programma di lettura mostra",
        "tutto il foglio, segni compresi, ed è ciò che la stampa vuole vedere.",
    ),
    second_note=(
        "La pagina 2 è lo stesso disegno con gli stessi riquadri, più un ritaglio sul "
        "rifilo: il programma di lettura",
        "mostra solo la pagina finita. Non si è tolto nulla: ciò che sta fuori è "
        "ancora qui.",
    ),
    moved_note=(
        "La pagina 4 è la metà destra di un foglio macchina largo due pagine: il suo "
        "angolo in basso a sinistra sta dove",
        "finisce la pagina 1, e il suo disegno e i suoi riquadri sono indicati da lì. "
        "Si vede come la pagina 1.",
    ),
    turned_title="Una pagina orizzontale in un documento verticale",
    across_before="Questa pagina è lo stesso foglio verticale delle due precedenti:",
    across_between=" punti di larghezza,",
    across_after=" di altezza. Ciò che",
    quarter="aggiunge è un numero che dice che il programma di lettura la ruota di un "
            "quarto in senso orario prima di mostrarla.",
    reading_software=(
        "Così le parole sono più larghe del foglio su cui sono scritte, e a nessuno "
        "va dato un",
        "secondo formato di pagina per dirlo. Stampando il file, il programma di "
        "lettura ruota la carta e non il",
        "disegno, e mostrandolo vi dà la pagina nel verso in cui è stata pensata.",
    ),
    in_file="come sta nel file",
    on_screen="come la mostra il programma di lettura",
    turn="un quarto in senso orario",
)

# The sheet in Spanish.
SPANISH = Words(
    title="Catálogo de otoño",
    band="La banda de arriba rebasa la cuchilla: no queda borde blanco.",
    blade=(
        "La cuchilla nunca cae exactamente donde se",
        "apunta. Una hoja lo prevé dos veces:",
        "la tinta pasa del corte, y nada que",
        "importe está a pocos puntos de él.",
    ),
    boxes=(
        "Las cajas dicen las tres distancias, así que nadie",
        "tiene que medir el archivo para saber dónde",
        "acaba de verdad la página.",
    ),
    rows=(
        "Papel de lino, resma",
        "Hilo de encuadernar",
        "Troqueladora de esquinas",
        "Galga de corte, acero",
        "Guía de sangrado, acrílico",
        "Limpiador de mantilla",
    ),
    foot=" — una hoja, cuatro cajas",
    guides=("sangrado", "corte", "arte"),
    first_note=(
        "La página 1 indica el sangrado, el corte y el arte, y ningún recorte: el "
        "programa de lectura muestra toda",
        "la hoja, marcas incluidas, que es lo que quiere ver la imprenta.",
    ),
    second_note=(
        "La página 2 es el mismo dibujo con las mismas cajas, más un recorte fijado "
        "al corte: el programa de lectura",
        "muestra solo la página acabada. No se quitó nada: lo de fuera sigue aquí.",
    ),
    moved_note=(
        "La página 4 es la mitad derecha de un pliego de dos páginas: su esquina "
        "inferior izquierda está donde",
        "acaba la página 1, y su dibujo y sus cajas se indican desde ahí. Se ve igual "
        "que la página 1.",
    ),
    turned_title="Una página apaisada dentro de un documento vertical",
    across_before="Esta página es la misma hoja vertical que las dos anteriores: ",
    across_between=" puntos de ancho, ",
    across_after=" de alto. Lo que",
    quarter="añade es un número que dice que el programa de lectura la gira un cuarto "
            "a la derecha antes de mostrarla.",
    reading_software=(
        "Así las palabras son más anchas que la hoja en que están escritas, y a nadie "
        "hay que darle un",
        "segundo tamaño de página para decirlo. Al imprimir el archivo, el programa "
        "de lectura gira el papel y no el",
        "dibujo, y al mostrarlo le da la página en el sentido en que se pensó.",
    ),
    in_file="tal como está en el archivo",
    on_screen="tal como lo muestra el programa de lectura",
    turn="un cuarto a la derecha",
)

# The sheet in German.
GERMAN = Words(
    title="Herbstprogramm",
    band="Das Band läuft über die Kante: Kein weißer Rand bleibt.",
    blade=(
        "Das Messer fällt nie genau dorthin, wo es",
        "hinzielt. Ein Bogen rechnet doppelt damit:",
        "Die Farbe läuft über den Schnitt, und nichts",
        "Wichtiges steht wenige Punkt davon entfernt.",
    ),
    boxes=(
        "Die Rahmen nennen alle drei Abstände, also",
        "muss niemand die Datei vermessen, um zu",
        "wissen, wo die Seite wirklich endet.",
    ),
    rows=(
        "Leinenpapier, Ries",
        "Buchbinderzwirn",
        "Eckenstanze",
        "Schneidlehre, Stahl",
        "Anschnittschablone, Acryl",
        "Gummituchwaschmittel",
    ),
    foot=" — ein Bogen, vier Rahmen",
    guides=("Anschnitt", "Endformat", "Motiv"),
    first_note=(
        "Seite 1 nennt Anschnitt, Endformat und Motiv, keinen Zuschnitt: Die "
        "Lesesoftware zeigt den",
        "ganzen Bogen samt Marken, und genau das will die Druckmaschine sehen.",
    ),
    second_note=(
        "Seite 2 ist dieselbe Zeichnung mit denselben Rahmen, dazu ein Zuschnitt aufs "
        "Endformat: Die",
        "Lesesoftware zeigt nur die fertige Seite. Nichts wurde entfernt — was außen "
        "ist, ist noch da.",
    ),
    moved_note=(
        "Seite 4 ist die rechte Hälfte eines zwei Seiten breiten Druckbogens: Ihre "
        "linke untere Ecke",
        "liegt, wo Seite 1 endet, und Zeichnung und Rahmen gelten ab dort. Sie "
        "erscheint wie Seite 1.",
    ),
    turned_title="Eine Querformatseite in einem Hochformatdokument",
    across_before="Diese Seite ist derselbe hochformatige Bogen wie die beiden davor: ",
    across_between=" Punkt breit, ",
    across_after=" hoch. Was",
    quarter="sie hinzufügt, ist eine Zahl: Die Lesesoftware dreht sie vor der Anzeige "
            "um eine Vierteldrehung.",
    reading_software=(
        "Also sind die Wörter breiter als der Bogen, auf dem sie stehen, und niemand "
        "muss dafür ein",
        "zweites Seitenformat bekommen. Eine Lesesoftware, die die Datei druckt, "
        "dreht das Papier statt",
        "der Zeichnung, und eine, die sie anzeigt, stellt die Seite so hin, wie sie "
        "gemeint war.",
    ),
    in_file="wie sie in der Datei steht",
    on_screen="wie die Lesesoftware sie zeigt",
    turn="eine Vierteldrehung im Uhrzeigersinn",
)

# 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 foot(words: Words) -> str:
    """The line of a page's foot, which names the company and what the page shows."""
    return f"{COMPANY}{words.foot}"


def how_wide(words: Words) -> str:
    """The first line of the turned page, which quotes the two measurements of the
    sheet it is drawn on rather than repeating them."""
    return (
        f"{words.across_before}{A4[0]:.0f}{words.across_between}"
        f"{A4[1]:.0f}{words.across_after}"
    )


def band():
    """The colour the band across the top is laid in."""
    return hqf_pdf.Rgb(BAND[0], BAND[1], BAND[2])


def trim():
    """Where the blade falls."""
    return hqf_pdf.Rect(TRIM[0], TRIM[1], TRIM[2], TRIM[3])


def bleed():
    """What the ink runs to."""
    return hqf_pdf.Rect(
        TRIM[0] - BLEED_OVER,
        TRIM[1] - BLEED_OVER,
        TRIM[2] + BLEED_OVER + BLEED_OVER,
        TRIM[3] + BLEED_OVER + BLEED_OVER,
    )


def moved(rect):
    """`rect` on the fourth sheet, whose corner sits at `MOVED`."""
    return hqf_pdf.Rect(rect.x + MOVED[0], rect.y + MOVED[1], rect.width, rect.height)


def art():
    """What is worth keeping of the page."""
    return hqf_pdf.Rect(
        TRIM[0] + ART_INSET,
        TRIM[1] + ART_INSET,
        TRIM[2] - ART_INSET - ART_INSET,
        TRIM[3] - ART_INSET - ART_INSET,
    )


def corner_marks(content):
    """Draw the four corner marks.

    Each pair of arms stands outside the ink and points at a corner of the
    finished page.
    """
    left, bottom, width, height = TRIM
    right = left + width
    top = bottom + height
    near = BLEED_OVER
    far = BLEED_OVER + MARK_LENGTH

    content.save_state()
    content.set_stroke(hqf_pdf.Color.gray(0.0))
    content.set_line_width(0.4)
    for x, from_x, to_x in [
        (left, left - near, left - far),
        (right, right + near, right + far),
    ]:
        for y, from_y, to_y in [
            (bottom, bottom - near, bottom - far),
            (top, top + near, top + far),
        ]:
            content.move_to(from_x, y)
            content.line_to(to_x, y)
            content.move_to(x, from_y)
            content.line_to(x, to_y)
    content.stroke()
    content.restore_state()


def listing(content, font, words, top, keep):
    """Draw the list itself, one row a line, each under a hairline."""
    right = keep.x + keep.width
    y = top
    for item, price in zip(words.rows, PRICES):
        content.save_state()
        content.set_fill(hqf_pdf.Color.gray(0.25))
        content.draw_text(font, BODY_SIZE, keep.x, y, item)
        content.draw_text(font, BODY_SIZE, right - PRICE_INSET, y, price)
        content.set_stroke(hqf_pdf.Color.gray(0.8))
        content.set_line_width(0.4)
        content.move_to(keep.x, y - 8.0)
        content.line_to(right, y - 8.0)
        content.stroke()
        content.restore_state()
        y -= 26.0


def artwork(content, font, words):
    """Draw the artwork: a band off the blade, a title, body, a list, a footer."""
    ink = bleed()
    keep = art()

    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.96))
    content.rect(ink.x, ink.y, ink.width, ink.height)
    content.fill()
    content.set_fill(band())
    content.rect(ink.x, ink.y + ink.height - 150.0, ink.width, 150.0)
    content.fill()
    content.restore_state()

    content.save_state()
    content.set_fill(hqf_pdf.Rgb(1.0, 1.0, 1.0))
    content.draw_text(
        font, TITLE_SIZE, keep.x, keep.y + keep.height - 30.0, words.title
    )
    content.set_fill(hqf_pdf.Rgb(0.82, 0.86, 0.92))
    content.draw_text(
        font, BODY_SIZE, keep.x, keep.y + keep.height - 50.0, words.band
    )
    content.restore_state()

    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.25))
    y = keep.y + keep.height - 130.0
    for paragraph in [words.blade, words.boxes]:
        for line in paragraph:
            content.draw_text(font, BODY_SIZE, keep.x, y, line)
            y -= BODY_STEP
        # The blank between two paragraphs is a step, not a line: an empty line drawn is
        # an empty text object written into the file.
        y -= BODY_STEP
    content.restore_state()

    listing(content, font, words, keep.y + keep.height - 290.0, keep)

    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.5))
    content.draw_text(font, FOOT_SIZE, keep.x, keep.y, foot(words))
    content.restore_state()


def guide_boxes():
    """The box each guide is drawn around, and the colour it is drawn in, in the order
    they are named."""
    return (
        (bleed(), hqf_pdf.Rgb(0.85, 0.35, 0.20)),
        (trim(), hqf_pdf.Rgb(0.20, 0.55, 0.35)),
        (art(), hqf_pdf.Rgb(0.45, 0.35, 0.65)),
    )


def guides(content, font, words):
    """Draw the guide showing where each box falls, and name it."""
    content.save_state()
    content.set_line_width(0.4)
    for (box, color), label in zip(guide_boxes(), words.guides):
        content.set_stroke(color)
        content.rect(box.x, box.y, box.width, box.height)
        content.stroke()
        content.set_fill(color)
        content.draw_text(
            font, NOTE_SIZE, box.x + box.width - GUIDE_INSET, box.y - 8.0, label
        )
    content.restore_state()


def sheet(font, words, note):
    """The whole sheet: artwork, marks, guides, and the note in the margin."""
    content = hqf_pdf.Content()
    draw_sheet(content, font, words, note)
    return content


def moved_sheet(font, words):
    """The same sheet drawn on the fourth page, whose corner sits at `MOVED`.

    One transform at the head of the stream carries the whole drawing there.
    """
    content = hqf_pdf.Content()
    content.transform((1.0, 0.0, 0.0, 1.0, MOVED[0], MOVED[1]))
    draw_sheet(content, font, words, words.moved_note)
    return content


def draw_sheet(content, font, words, note):
    """Draw the artwork, the marks, the guides and the note in the margin, in the
    sheet's own coordinates."""
    artwork(content, font, words)
    corner_marks(content)
    guides(content, font, words)

    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.35))
    y = NOTE_BASELINE
    for line in reversed(note):
        content.draw_text(font, NOTE_SIZE, TRIM[0], y, line)
        y += NOTE_STEP
    content.restore_state()


def sketch(content, font, corner, caption):
    """Draw one sheet of the little diagram.

    A sheet taller than it is wide is one reading software has not turned yet, so the
    head of the drawing runs down its left edge; a wide one has been turned, and
    the head is along its top.
    """
    x, y, width, height = corner
    if height > width:
        head = (x + 8.0, y + 8.0, 12.0, height - 16.0)
    else:
        head = (x + 8.0, y + height - 20.0, width - 16.0, 12.0)

    content.save_state()
    content.set_stroke(hqf_pdf.Color.gray(0.55))
    content.set_line_width(0.8)
    content.rect(x, y, width, height)
    content.stroke()
    content.set_fill(band())
    content.rect(head[0], head[1], head[2], head[3])
    content.fill()
    content.set_fill(hqf_pdf.Color.gray(0.45))
    content.draw_text(font, CAPTION_SIZE, x, y - 14.0, caption)
    content.restore_state()


def turned(font, words):
    """A landscape page inside an upright document.

    Everything is laid out in a space as wide as the sheet is tall, and the one
    transform at the head of the stream puts that space onto the upright sheet.
    """
    across, up = A4[1], A4[0]
    content = hqf_pdf.Content()
    content.transform((0.0, 1.0, -1.0, 0.0, A4[0], 0.0))

    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.96))
    content.rect(0.0, 0.0, across, up)
    content.fill()
    content.set_fill(band())
    content.rect(0.0, up - 90.0, across, 90.0)
    content.fill()
    content.restore_state()

    content.save_state()
    content.set_fill(hqf_pdf.Rgb(1.0, 1.0, 1.0))
    content.draw_text(
        font, TURNED_TITLE_SIZE, TURNED_LEFT, up - 55.0, words.turned_title
    )
    content.restore_state()

    content.save_state()
    content.set_fill(hqf_pdf.Color.gray(0.3))
    y = up - 130.0
    for paragraph in [(how_wide(words), words.quarter), words.reading_software]:
        for line in paragraph:
            content.draw_text(font, BODY_SIZE, TURNED_LEFT, y, line)
            y -= BODY_STEP
        # The blank between two paragraphs is a step, not a line: an empty line drawn is
        # an empty text object written into the file.
        y -= BODY_STEP
    content.restore_state()

    for corner, caption in zip(SKETCHES, (words.in_file, words.on_screen)):
        sketch(content, font, corner, caption)

    content.save_state()
    content.set_stroke(hqf_pdf.Color.gray(0.55))
    content.set_line_width(1.2)
    content.move_to(295.0, 218.0)
    content.line_to(360.0, 218.0)
    content.line_to(348.0, 226.0)
    content.move_to(360.0, 218.0)
    content.line_to(348.0, 210.0)
    content.stroke()
    content.set_fill(hqf_pdf.Color.gray(0.45))
    content.draw_text(font, CAPTION_SIZE, ARROW_LABEL_LEFT, 234.0, words.turn)
    content.restore_state()
    return content


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

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

    first = hqf_pdf.Page(SHEET[0], SHEET[1])
    first.set_content(sheet(font, words, words.first_note))
    first.set_bleed_box(bleed())
    first.set_trim_box(trim())
    first.set_art_box(art())
    document.add_page(first)

    second = hqf_pdf.Page(SHEET[0], SHEET[1])
    second.set_content(sheet(font, words, words.second_note))
    second.set_crop_box(trim())
    second.set_bleed_box(bleed())
    second.set_trim_box(trim())
    second.set_art_box(art())
    document.add_page(second)

    third = hqf_pdf.Page.a4()
    third.set_content(turned(font, words))
    third.set_turn(hqf_pdf.PageTurn.Right)
    document.add_page(third)

    fourth = hqf_pdf.Page(SHEET[0], SHEET[1])
    fourth.set_origin(MOVED[0], MOVED[1])
    fourth.set_content(moved_sheet(font, words))
    fourth.set_bleed_box(moved(bleed()))
    fourth.set_trim_box(moved(trim()))
    fourth.set_art_box(moved(art()))
    document.add_page(fourth)

    written = document.write(out)
    print(
        f"wrote {out}: {written} bytes, one drawing on {document.page_count} "
        f"pages, each stating its own boxes"
    )


if __name__ == "__main__":
    main()
