"""Sets text in a font whose letters are drawings rather than outlines read from a file.

The Python twin of the `write_drawn_letters` example in Rust. Two fonts are built here,
both out of content operators. The first paints its own colours: a green tick, a red
cross and a grey dash, set inline among ordinary words. The second states shapes and no
colour, so each is painted in whatever colour the text around it is in — the same square
comes out red on one line and blue on the next.

A font file that paints its own signs in colour is then set beside them: three of its
signs inside a sentence, its star from each of the two palettes the file holds, and its
flag, whose pole takes the colour of the text around it while its cloth keeps its own.

The letters still copy out of the file as the characters they stand for, because the
font carries the map back to them.

Under the note, each font says what it holds: how many glyphs, the box they stand in,
the matrix that scales them, and which glyph its first mark is and how wide.

Every word the page sets is held in `Words`, once per language, and `HQF_PDF_LANG`
picks which set is drawn.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf


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

    # The page's title.
    title: str
    # What the page is about.
    lead: str
    # The heading over the letters that paint themselves.
    painted: str
    # The line the coloured letters are set in, before each of them.
    checks: tuple[str, str, str]
    # The heading over the letters that take the colour around them.
    shaped: str
    # What the two coloured lines say, before the square.
    tinted: tuple[str, str]
    # The closing note about copying the text out.
    note: str
    # What the two fonts are called on the page, the painted one first.
    fonts: tuple[str, str]
    # What a font holds; `{font}`, `{count}`, `{box}`, `{matrix}`, `{character}`,
    # `{index}` and `{width}` stand for them.
    holds: str
    # The heading over the signs a font file paints in colour.
    colored: str
    # The start of the sentence the three coloured signs are set in.
    steps_lead: str
    # What follows each of the three coloured signs in that sentence.
    steps: tuple[str, str, str]
    # What follows the star from each palette, the first palette first.
    palettes: tuple[str, str]
    # The note about the signs the font file paints.
    colored_note: str


# The page in English.
ENGLISH = Words(
    title="Letters you draw yourself",
    lead=(
        "The three marks below are not in any font file. Each one is a drawing this "
        "program made, added to the document as a font, and set in a line of text like "
        "any other letter. Copy the line out of this file and the marks come back as "
        "the characters they stand for."
    ),
    painted="Marks that paint themselves",
    checks=(
        "Invoice sent",
        "Payment received",
        "Delivery note",
    ),
    shaped="Marks that take the colour around them",
    tinted=(
        "Overdue",
        "Settled",
    ),
    note=(
        "A mark of the first kind carries its own colour. A mark of the second kind "
        "states a shape and nothing else, so the colour of the words it sits among is "
        "the colour it comes out in."
    ),
    fonts=(
        "The painted font",
        "The shaped font",
    ),
    holds=(
        "{font}: glyphs, {count}; box, [{box}]; matrix, [{matrix}]; {character} is its "
        "glyph {index}, {width} wide."
    ),
    colored="Signs a font file paints in colour",
    steps_lead="Each step is marked: ",
    steps=(
        "approved,",
        "under review,",
        "awaiting signature.",
    ),
    palettes=(
        "The star from the font’s first palette;",
        "the same star from its second.",
    ),
    colored_note=(
        "This program draws none of these signs: the font file paints each one in "
        "layers of colour, and the document sets them like any other letter. The "
        "flag’s pole takes the colour of the text around it; its cloth keeps its own."
    ),
)

# The page in French.
FRENCH = Words(
    title="Des lettres que vous dessinez vous-même",
    lead=(
        "Les trois marques ci-dessous ne viennent d’aucun fichier de police. Chacune "
        "est un dessin créé par ce programme, ajouté au document comme une police et "
        "posé dans une ligne de texte comme n’importe quelle lettre. Copiez la ligne "
        "depuis ce fichier : les marques redeviennent les caractères qu’elles "
        "représentent."
    ),
    painted="Des marques qui ont leur propre couleur",
    checks=(
        "Facture envoyée",
        "Paiement reçu",
        "Bon de livraison",
    ),
    shaped="Des marques qui prennent la couleur qui les entoure",
    tinted=(
        "En retard",
        "Réglé",
    ),
    note=(
        "Une marque du premier type définit sa propre couleur. Une marque du second "
        "type ne définit qu’une forme : elle prend donc la couleur des mots qu’elle "
        "accompagne."
    ),
    fonts=(
        "La police peinte",
        "La police de formes",
    ),
    holds=(
        "{font} : glyphes, {count} ; boîte, [{box}] ; matrice, [{matrix}] ; "
        "{character} est son glyphe {index}, large de {width}."
    ),
    colored="Des signes qu’un fichier de police peint en couleur",
    steps_lead="Chaque étape est signalée : ",
    steps=(
        "approuvée,",
        "en cours de relecture,",
        "en attente de signature.",
    ),
    palettes=(
        "L’étoile tirée de la première palette de la police ;",
        "la même étoile tirée de la deuxième.",
    ),
    colored_note=(
        "Ce programme ne dessine aucun de ces signes : le fichier de police peint "
        "chacun d’eux en couches de couleur, et le document les pose comme n’importe "
        "quelle lettre. Le mât du drapeau prend la couleur du texte qui l’entoure ; le "
        "tissu garde la sienne."
    ),
)

# The page in Dutch.
DUTCH = Words(
    title="Letters die u zelf tekent",
    lead=(
        "De drie tekens hieronder staan in geen enkel lettertypebestand. Elk is een "
        "tekening die dit programma heeft gemaakt, als lettertype aan het document "
        "toegevoegd en in een tekstregel gezet zoals elke andere letter. Kopieer de "
        "regel uit dit bestand en de tekens komen terug als de tekens waar ze voor "
        "staan."
    ),
    painted="Tekens die zichzelf kleuren",
    checks=(
        "Factuur verstuurd",
        "Betaling ontvangen",
        "Pakbon",
    ),
    shaped="Tekens die de kleur om zich heen aannemen",
    tinted=(
        "Achterstallig",
        "Voldaan",
    ),
    note=(
        "Een teken van de eerste soort heeft zijn eigen kleur. Een teken van de tweede "
        "soort geeft een vorm en verder niets, dus krijgt het de kleur van de woorden "
        "waartussen het staat."
    ),
    fonts=(
        "Het geschilderde lettertype",
        "Het vormlettertype",
    ),
    holds=(
        "{font}: glyphs, {count}; kader, [{box}]; matrix, [{matrix}]; {character} is "
        "zijn glyph {index}, {width} breed."
    ),
    colored="Tekens die een lettertypebestand in kleur schildert",
    steps_lead="Elke stap krijgt een teken: ",
    steps=(
        "goedgekeurd,",
        "in beoordeling,",
        "wacht op ondertekening.",
    ),
    palettes=(
        "De ster uit het eerste palet van het lettertype;",
        "dezelfde ster uit het tweede.",
    ),
    colored_note=(
        "Dit programma tekent geen van deze tekens: het lettertypebestand schildert "
        "elk teken in kleurlagen, en het document zet ze net als elke andere letter. "
        "De stok van de vlag neemt de kleur van de omringende tekst aan; het doek "
        "houdt zijn eigen kleur."
    ),
)

# The page in Portuguese.
PORTUGUESE = Words(
    title="Letras desenhadas por si",
    lead=(
        "As três marcas abaixo não estão em nenhum ficheiro de tipo de letra. Cada "
        "uma é um desenho feito por este programa, acrescentado ao documento como "
        "tipo de letra e composto numa linha de texto como qualquer outra letra. "
        "Copie a linha deste ficheiro e as marcas voltam como os caracteres que "
        "representam."
    ),
    painted="Marcas que se pintam sozinhas",
    checks=(
        "Fatura enviada",
        "Pagamento recebido",
        "Guia de remessa",
    ),
    shaped="Marcas que tomam a cor à sua volta",
    tinted=(
        "Vencida",
        "Liquidada",
    ),
    note=(
        "Uma marca do primeiro tipo leva a sua própria cor. Uma marca do segundo tipo "
        "dá uma forma e nada mais, por isso sai da cor das palavras entre as quais "
        "está."
    ),
    fonts=(
        "O tipo de letra pintado",
        "O tipo de letra de formas",
    ),
    holds=(
        "{font}: glifos, {count}; caixa, [{box}]; matriz, [{matrix}]; {character} é o "
        "seu glifo {index}, de largura {width}."
    ),
    colored="Sinais que um ficheiro de tipo de letra pinta a cores",
    steps_lead="Cada etapa tem o seu sinal: ",
    steps=(
        "aprovada,",
        "em análise,",
        "por assinar.",
    ),
    palettes=(
        "A estrela na primeira paleta do tipo de letra;",
        "a mesma estrela na segunda.",
    ),
    colored_note=(
        "Este programa não desenha nenhum destes sinais: o ficheiro de tipo de letra "
        "pinta cada um em camadas de cor e o documento compõe-nos como qualquer outra "
        "letra. O mastro da bandeira toma a cor do texto que o rodeia; o pano conserva "
        "a sua."
    ),
)

# The page in Italian.
ITALIAN = Words(
    title="Lettere disegnate da voi",
    lead=(
        "I tre segni qui sotto non sono in nessun file di font. Ciascuno è un disegno "
        "fatto da questo programma, aggiunto al documento come font e composto in una "
        "riga di testo come ogni altra lettera. Copiate la riga da questo file e i "
        "segni tornano come i caratteri che rappresentano."
    ),
    painted="Segni che si dipingono da sé",
    checks=(
        "Fattura inviata",
        "Pagamento ricevuto",
        "Documento di trasporto",
    ),
    shaped="Segni che prendono il colore intorno",
    tinted=(
        "Scaduta",
        "Saldata",
    ),
    note=(
        "Un segno del primo tipo porta il proprio colore. Un segno del secondo tipo dà "
        "una forma e nient’altro, così esce del colore delle parole tra cui si trova."
    ),
    fonts=(
        "Il font dipinto",
        "Il font di forme",
    ),
    holds=(
        "{font}: glifi, {count}; riquadro, [{box}]; matrice, [{matrix}]; {character} "
        "è il suo glifo {index}, largo {width}."
    ),
    colored="Segni che un file di font dipinge a colori",
    steps_lead="Ogni fase ha il suo segno: ",
    steps=(
        "approvata,",
        "in revisione,",
        "in attesa di firma.",
    ),
    palettes=(
        "La stella con la prima palette del font;",
        "la stessa stella con la seconda.",
    ),
    colored_note=(
        "Questo programma non disegna nessuno di questi segni: è il file di font a "
        "dipingerli, ciascuno a strati di colore, e il documento li compone come "
        "qualsiasi altra lettera. L’asta della bandiera prende il colore del testo "
        "intorno; il drappo conserva il proprio."
    ),
)

# The page in Spanish.
SPANISH = Words(
    title="Letras que dibuja usted mismo",
    lead=(
        "Las tres marcas de abajo no están en ningún archivo de fuente. Cada una es "
        "un dibujo hecho por este programa, añadido al documento como fuente y "
        "compuesto en una línea de texto como cualquier otra letra. Copie la línea de "
        "este archivo y las marcas vuelven como los caracteres que representan."
    ),
    painted="Marcas que se pintan solas",
    checks=(
        "Factura enviada",
        "Pago recibido",
        "Albarán",
    ),
    shaped="Marcas que toman el color de su entorno",
    tinted=(
        "Vencida",
        "Saldada",
    ),
    note=(
        "Una marca del primer tipo lleva su propio color. Una marca del segundo tipo "
        "da una forma y nada más, así que toma el color de las palabras entre las que "
        "está."
    ),
    fonts=(
        "La fuente pintada",
        "La fuente de formas",
    ),
    holds=(
        "{font}: glifos, {count}; caja, [{box}]; matriz, [{matrix}]; {character} es "
        "su glifo {index}, de ancho {width}."
    ),
    colored="Signos que un archivo de fuente pinta en color",
    steps_lead="Cada fase se indica con un signo: ",
    steps=(
        "aprobada,",
        "en revisión,",
        "pendiente de firma.",
    ),
    palettes=(
        "La estrella con la primera paleta de la fuente;",
        "la misma estrella con la segunda.",
    ),
    colored_note=(
        "Este programa no dibuja ninguno de estos signos: el archivo de fuente pinta "
        "cada uno en capas de color, y el documento los compone como cualquier otra "
        "letra. El asta de la bandera toma el color del texto que la rodea; la tela "
        "conserva el suyo."
    ),
)

# The page in German.
GERMAN = Words(
    title="Buchstaben, die Sie selbst zeichnen",
    lead=(
        "Die drei Zeichen unten stehen in keiner Schriftdatei. Jedes ist eine "
        "Zeichnung, die dieses Programm angefertigt, dem Dokument als Schrift "
        "hinzugefügt und wie jeden anderen Buchstaben in eine Textzeile gesetzt hat. "
        "Kopieren Sie die Zeile aus dieser Datei, und die Zeichen kommen als die "
        "Schriftzeichen zurück, für die sie stehen."
    ),
    painted="Zeichen, die sich selbst färben",
    checks=(
        "Rechnung versandt",
        "Zahlung eingegangen",
        "Lieferschein",
    ),
    shaped="Zeichen, die die Farbe ihrer Umgebung annehmen",
    tinted=(
        "Überfällig",
        "Beglichen",
    ),
    note=(
        "Ein Zeichen der ersten Art trägt seine eigene Farbe. Ein Zeichen der zweiten "
        "Art gibt eine Form an und sonst nichts, also erscheint es in der Farbe der "
        "Wörter, zwischen denen es steht."
    ),
    fonts=(
        "Die gefärbte Schrift",
        "Die geformte Schrift",
    ),
    holds=(
        "{font}: Glyphen, {count}; Rahmen, [{box}]; Matrix, [{matrix}]; {character} "
        "ist ihr Glyph {index}, {width} breit."
    ),
    colored="Zeichen, die eine Schriftdatei in Farbe malt",
    steps_lead="Jeder Schritt ist gekennzeichnet: ",
    steps=(
        "freigegeben,",
        "in Prüfung,",
        "Unterschrift ausstehend.",
    ),
    palettes=(
        "Der Stern aus der ersten Palette der Schrift;",
        "derselbe Stern aus ihrer zweiten.",
    ),
    colored_note=(
        "Keines dieser Zeichen zeichnet dieses Programm: Die Schriftdatei malt jedes "
        "in Farbschichten, und das Dokument setzt sie wie jeden anderen Buchstaben. "
        "Die Fahnenstange nimmt die Farbe des umgebenden Textes an; das Fahnentuch "
        "behält seine eigene."
    ),
)

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

# The matrix that puts a drawn font on the thousandth scale a font file uses, so a width
# of 600 is six tenths of the size the text is set at.
THOUSANDTH = (0.001, 0.0, 0.0, 0.001, 0.0, 0.0)

# How wide every drawn glyph is, in glyph space: one em.
GLYPH_WIDTH = 1000.0

# How thick a drawn stroke is, in glyph space.
STROKE = 150.0

# The left edge of everything on the page.
LEFT = 72.0

# How wide a block of text is.
WIDTH = 450.0

# The character the tick is set with.
TICK = "✓"
# The character the cross is set with.
CROSS = "✗"
# The character the dash is set with.
DASH = "–"
# The character the square is set with.
SQUARE = "■"

# The star the colour font paints.
STAR = "★"
# The target the colour font paints.
TARGET = "◉"
# The triangle the colour font paints.
TRIANGLE = "▲"
# The flag the colour font paints, its pole in the colour of the text.
FLAG = "⚑"

# Every sign the page sets in the colour font.
SIGNS = (STAR, TARGET, TRIANGLE, FLAG)

# The font file that paints its signs in colour: the one committed for the tests.
COLOR_FONT = _out.ROOT / "crates" / "hqf-pdf" / "tests" / "fonts" / "HqfColor.ttf"


def glyph_box() -> hqf_pdf.Rect:
    """The box every drawn glyph stands in, in glyph space."""
    return hqf_pdf.Rect(0.0, -200.0, GLYPH_WIDTH, 1000.0)


def tick(ink: hqf_pdf.Rgb) -> hqf_pdf.Content:
    """A tick, stroked in `ink`."""
    pen = hqf_pdf.Content()
    pen.set_stroke(ink)
    pen.set_line_width(STROKE)
    pen.move_to(140.0, 480.0)
    pen.line_to(390.0, 230.0)
    pen.line_to(870.0, 740.0)
    pen.stroke()
    return pen


def cross(ink: hqf_pdf.Rgb) -> hqf_pdf.Content:
    """A cross, stroked in `ink`."""
    pen = hqf_pdf.Content()
    pen.set_stroke(ink)
    pen.set_line_width(STROKE)
    pen.move_to(200.0, 230.0)
    pen.line_to(810.0, 740.0)
    pen.move_to(810.0, 230.0)
    pen.line_to(200.0, 740.0)
    pen.stroke()
    return pen


def dash(ink: hqf_pdf.Rgb) -> hqf_pdf.Content:
    """A dash, filled in `ink`."""
    pen = hqf_pdf.Content()
    pen.set_fill(ink)
    pen.rect(160.0, 400.0, 680.0, STROKE)
    pen.fill()
    return pen


def square() -> hqf_pdf.Content:
    """A filled square, in no colour of its own."""
    pen = hqf_pdf.Content()
    pen.rect(200.0, 150.0, 600.0, 600.0)
    pen.fill()
    return pen


def tick_glyph() -> hqf_pdf.Type3Glyph:
    """The tick glyph, green."""
    return hqf_pdf.Type3Glyph.painted(
        TICK, GLYPH_WIDTH, tick(hqf_pdf.Rgb(0.15, 0.55, 0.25))
    )


def square_glyph() -> hqf_pdf.Type3Glyph:
    """The square glyph, in no colour of its own."""
    bounds = hqf_pdf.Rect(200.0, 150.0, 600.0, 600.0)
    return hqf_pdf.Type3Glyph.shaped(SQUARE, GLYPH_WIDTH, bounds, square())


def number(value: float) -> str:
    """`value` written the shortest way that reads back as it, a whole number without
    its point."""
    return str(int(value)) if value == int(value) else repr(value)


def listed(values) -> str:
    """The numbers of `values` as the page writes them, a space between each."""
    return " ".join(number(value) for value in values)


def holds(font, first, name: str, words: Words) -> str:
    """What `font`, called `name`, holds, its first mark being `first`."""
    shape = font.box
    glyph = font.glyph(first.character)
    index = "" if glyph is None else str(glyph)
    return (
        words.holds.replace("{font}", name)
        .replace("{count}", str(font.glyph_count))
        .replace("{box}", listed((shape.x, shape.y, shape.width, shape.height)))
        .replace("{matrix}", listed(font.matrix))
        .replace("{character}", first.character)
        .replace("{index}", index)
        .replace("{width}", number(first.width))
    )


def painted_font() -> hqf_pdf.Type3Font:
    """The font whose three marks paint themselves."""
    glyphs = [
        tick_glyph(),
        hqf_pdf.Type3Glyph.painted(
            CROSS, GLYPH_WIDTH, cross(hqf_pdf.Rgb(0.75, 0.15, 0.15))
        ),
        hqf_pdf.Type3Glyph.painted(DASH, GLYPH_WIDTH, dash(hqf_pdf.Rgb.gray(0.55))),
    ]
    return hqf_pdf.Type3Font(THOUSANDTH, glyph_box(), glyphs)


def shaped_font() -> hqf_pdf.Type3Font:
    """The font whose one mark takes the colour of the text around it."""
    return hqf_pdf.Type3Font(THOUSANDTH, glyph_box(), [square_glyph()])


def block(
    content: hqf_pdf.Content,
    handle: hqf_pdf.FontHandle,
    size: float,
    top: float,
    text: str,
) -> float:
    """Sets a block of words at `top`, and hands back the ordinate it ends at."""
    flow = hqf_pdf.TextFlow(handle, size)
    lines = flow.break_lines(text, WIDTH)
    # The binding opens the text object itself, so opening another here would write a
    # pair of operators its twin in Rust does not.
    flow.draw(content, lines, LEFT, top, WIDTH)
    return top - flow.height(lines)


def set_line(content: hqf_pdf.Content, line: hqf_pdf.RichText, top: float) -> float:
    """Sets `line` with its first baseline at `top`, and hands back the ordinate it
    ends at."""
    lines = line.break_lines(WIDTH)
    line.draw(content, lines, LEFT, top, WIDTH)
    return top - hqf_pdf.RichText.height(lines)


def color_signs(
    content: hqf_pdf.Content,
    words: Words,
    text: hqf_pdf.FontHandle,
    fonts: tuple[hqf_pdf.FontHandle, hqf_pdf.FontHandle],
    top: float,
) -> float:
    """Sets the signs the colour font paints, its heading at `top`: three inside a
    sentence, the star from each palette, the flag in two text colours, then the note
    under them. `fonts` holds the colour font from its first palette, then from its
    second."""
    top = block(content, text, 12.0, top, words.colored) - 10.0
    label = hqf_pdf.Style(text, 11.0)
    sign = hqf_pdf.Style(fonts[0], 11.0)
    sentence = hqf_pdf.RichText().span(words.steps_lead, label)
    for index, step in enumerate(words.steps):
        if index > 0:
            sentence = sentence.span(" ", label)
        shown = (STAR, TARGET, TRIANGLE)[index]
        sentence = sentence.span(shown, sign).span(f" {step}", label)
    top = set_line(content, sentence, top) - 8.0

    palettes = (
        hqf_pdf.RichText()
        .span(STAR, sign)
        .span(f" {words.palettes[0]}", label)
        .span(" ", label)
        .span(STAR, hqf_pdf.Style(fonts[1], 11.0))
        .span(f" {words.palettes[1]}", label)
    )
    top = set_line(content, palettes, top) - 8.0

    flags = hqf_pdf.RichText()
    for index, tinted in enumerate(words.tinted):
        ink = hqf_pdf.Rgb(0.75, 0.15, 0.15) if index == 0 else hqf_pdf.Rgb(0.15, 0.35, 0.75)
        if index > 0:
            flags = flags.span("    ", label)
        flags = flags.span(FLAG, hqf_pdf.Style(fonts[0], 11.0, color=ink)).span(
            f" {tinted}", hqf_pdf.Style(text, 11.0, color=ink)
        )
    top = set_line(content, flags, top) - 14.0
    return block(content, text, 10.0, top, words.colored_note)


def main() -> None:
    """Writes the page."""
    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("drawn_letters.pdf", language)).stem
    )

    doc = hqf_pdf.Document()
    doc.set_license(_licence.licensed())
    text = doc.add_font(hqf_pdf.Font.from_path(_out.font_path()))
    painted, shaped = painted_font(), shaped_font()
    said = [
        holds(painted, tick_glyph(), words.fonts[0], words),
        holds(shaped, square_glyph(), words.fonts[1], words),
    ]
    marks = doc.add_type3_font(painted)
    shapes = doc.add_type3_font(shaped)
    colored = hqf_pdf.Font.from_path(COLOR_FONT)
    signs = doc.add_color_font(hqf_pdf.ColorFont(colored, "".join(SIGNS)))
    dark = doc.add_color_font(hqf_pdf.ColorFont(colored, STAR).with_palette(1))

    content = hqf_pdf.Content()

    top = 760.0
    top = block(content, text, 18.0, top, words.title) - 14.0
    top = block(content, text, 10.0, top, words.lead) - 26.0

    top = block(content, text, 12.0, top, words.painted) - 10.0
    label = hqf_pdf.Style(text, 11.0)
    mark = hqf_pdf.Style(marks, 11.0)
    line = hqf_pdf.RichText()
    for index, check in enumerate(words.checks):
        drawn = (TICK, CROSS, DASH)[index]
        # The gap after a mark is set in the words' own font: a font of drawings holds
        # the marks and nothing else, so a space in it would be a space it cannot draw.
        line = line.span(drawn, mark).span(f" {check}", label)
        line = line.span("" if index == len(words.checks) - 1 else "    ", label)
    lines = line.break_lines(WIDTH)
    line.draw(content, lines, LEFT, top, WIDTH)
    top -= hqf_pdf.RichText.height(lines) + 26.0

    top = block(content, text, 12.0, top, words.shaped) - 10.0
    for index, tinted in enumerate(words.tinted):
        ink = hqf_pdf.Rgb(0.75, 0.15, 0.15) if index == 0 else hqf_pdf.Rgb(0.15, 0.35, 0.75)
        shape = hqf_pdf.Style(shapes, 11.0, color=ink)
        word = hqf_pdf.Style(text, 11.0, color=ink)
        row = hqf_pdf.RichText().span(SQUARE, shape).span(f" {tinted}", word)
        drawn = row.break_lines(WIDTH)
        row.draw(content, drawn, LEFT, top, WIDTH)
        top -= hqf_pdf.RichText.height(drawn) + 4.0

    top -= 22.0
    top = block(content, text, 10.0, top, words.note) - 14.0
    for line in said:
        top = block(content, text, 9.0, top, line) - 6.0

    color_signs(content, words, text, (signs, dark), top - 20.0)

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

    written = doc.write(out)
    print(f"wrote {out}: {written} bytes")


if __name__ == "__main__":
    main()
