"""Sets text in an embedded font, and writes the result to disk.

The Python twin of the `write_text` example in Rust: the same PDF, the same
words, through the binding rather than through the library directly.

`pdftotext` must be able to pull the words back out of the result. If it cannot,
the map back to characters is wrong and the text is unsearchable and uncopyable
even though the page looks right.

Under the invoice, four lines are set with the text state rather than with coordinates:
they step down by the leading, one has its letters pushed apart, one its glyphs
narrowed, and the rule under them is a stream of its own appended to the page's.

Once the file is written, the font is told to keep every glyph, and the terminal says
what the same document weighs with the face carried whole rather than cut down to what
the page draws.

The words are held in `Words`, once per language, and `HQF_PDF_LANG` picks which set is
drawn. The number, the issuer and the three amounts are the same in every language.

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

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import _language
import _licence
import _out

import hqf_pdf

# The invoice's number.
NUMBER = "1964413"

# Who issued it.
ISSUER = "HQF Development, Cabriès"

# What each of the three billed lines comes to, in the order they are set.
AMOUNTS = ("4 250.00 EUR", "850.00 EUR", "5 100.00 EUR")

# How many characters a billed line runs to: its label, a space, the leader dots, a
# space, then its amount. The dots take whatever is left, so the amounts end on the same
# character however long the label before them is.
LINE_CHARS = 57


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

    What is not language stays out of it: the invoice's number, who issued it and the
    three amounts read the same wherever the page is read.
    """

    # The document's title, which reading software shows over the page.
    title: str
    # What stands before the invoice's number.
    number_label: str
    # The line that gives the day it was issued and the term it falls due in.
    dates: str
    # What each billed line is called, in the order AMOUNTS prices them.
    items: tuple[str, str, str]
    # The line that says the lines under it step down by the leading.
    leading: str
    # The line set with its letters pushed apart.
    spaced: str
    # The line set with its glyphs narrowed.
    narrowed: str
    # The line that keeps the leading the move above it set.
    stepped: str

    def lines(self) -> list[str]:
        """Return the lines the sample page sets, which pdftotext must find again.

        They are not all spelled out of the ASCII range for the look of it: an accent
        and a dash are two glyphs a wrong ToUnicode map loses first, and a page that
        only ever said "Total" would never catch it.
        """
        lines = [f"{self.number_label} {NUMBER} — {ISSUER}", self.dates]
        for item, amount in zip(self.items, AMOUNTS):
            dots = max(0, LINE_CHARS - len(item) - len(amount) - 2)
            lines.append(f"{item} {'.' * dots} {amount}")
        return lines


# The page in English.
ENGLISH = Words(
    title="hqf-pdf text sample",
    number_label="Invoice No.",
    dates="Issued 14 July 2026 — due within 30 days",
    items=(
        "Rendering engine development",
        "VAT at 20 %",
        "Total due",
    ),
    leading="Each line below steps down by the leading, not by a coordinate.",
    spaced="These letters stand one and a half points apart.",
    narrowed="These glyphs are narrowed to eighty per cent of their width.",
    stepped="This line keeps the leading the move above it set: twenty-two points.",
)

# The page in French.
FRENCH = Words(
    title="Échantillon de texte hqf-pdf",
    number_label="Facture n°",
    dates="Émise le 14 juillet 2026 — payable sous 30 jours",
    items=(
        "Développement du moteur de rendu",
        "TVA 20 %",
        "Total à payer",
    ),
    leading=(
        "Chaque ligne ci-dessous descend de l’interligne, et non d’une coordonnée."
    ),
    spaced="Ces lettres sont écartées d’un point et demi.",
    narrowed="Ces glyphes sont ramenés à quatre-vingts pour cent de leur largeur.",
    stepped=(
        "Cette ligne garde l’interligne fixé par le déplacement du dessus : vingt-deux "
        "points."
    ),
)

# The page in Dutch.
DUTCH = Words(
    title="hqf-pdf-tekstvoorbeeld",
    number_label="Factuur nr.",
    dates="Uitgegeven op 14 juli 2026 — te betalen binnen 30 dagen",
    items=(
        "Ontwikkeling van de opmaakmotor",
        "Btw 20 %",
        "Te betalen",
    ),
    leading="Elke regel hieronder zakt met de regelafstand, niet met een coördinaat.",
    spaced="Deze letters staan anderhalve punt uit elkaar.",
    narrowed="Deze glyphs zijn versmald tot tachtig procent van hun breedte.",
    stepped="Deze regel houdt de regelafstand van de stap erboven: tweeëntwintig punten.",
)

# The page in Portuguese.
PORTUGUESE = Words(
    title="Amostra de texto hqf-pdf",
    number_label="Fatura n.º",
    dates="Emitida a 14 de julho de 2026 — vence a 30 dias",
    items=(
        "Desenvolvimento do motor de paginação",
        "IVA a 20 %",
        "Total a pagar",
    ),
    leading="Cada linha abaixo desce pelo entrelinhamento, não por uma coordenada.",
    spaced="Estas letras estão a ponto e meio umas das outras.",
    narrowed="Estes glifos são estreitados para oitenta por cento da sua largura.",
    stepped="Esta linha mantém o entrelinhamento do passo acima: vinte e dois pontos.",
)

# The page in Italian.
ITALIAN = Words(
    title="Campione di testo hqf-pdf",
    number_label="Fattura n.",
    dates="Emessa il 14 luglio 2026 — scadenza a 30 giorni",
    items=(
        "Sviluppo del motore di impaginazione",
        "IVA al 20 %",
        "Totale dovuto",
    ),
    leading="Ogni riga qui sotto scende dell’interlinea, non di una coordinata.",
    spaced="Queste lettere stanno a un punto e mezzo l’una dall’altra.",
    narrowed="Questi glifi sono ristretti all’ottanta per cento della loro larghezza.",
    stepped="Questa riga conserva l’interlinea fissata sopra: ventidue punti.",
)

# The page in Spanish.
SPANISH = Words(
    title="Muestra de texto de hqf-pdf",
    number_label="Factura n.º",
    dates="Emitida el 14 de julio de 2026 — vence a 30 días",
    items=(
        "Desarrollo del motor de maquetación",
        "IVA al 20 %",
        "Total a pagar",
    ),
    leading="Cada línea de abajo baja según el interlineado, no según una coordenada.",
    spaced="Estas letras están a punto y medio unas de otras.",
    narrowed="Estos glifos se estrechan al ochenta por ciento de su ancho.",
    stepped="Esta línea conserva el interlineado del paso de arriba: veintidós puntos.",
)

# The page in German.
GERMAN = Words(
    title="hqf-pdf-Textbeispiel",
    number_label="Rechnung Nr.",
    dates="Ausgestellt am 14. Juli 2026 — zahlbar innerhalb von 30 Tagen",
    items=(
        "Entwicklung der Rendering-Engine",
        "MwSt. 20 %",
        "Zu zahlen",
    ),
    leading="Jede Zeile unten rückt um den Zeilenabstand, nicht um eine Koordinate.",
    spaced="Diese Buchstaben stehen anderthalb Punkt auseinander.",
    narrowed="Diese Glyphen sind auf achtzig Prozent ihrer Breite verengt.",
    stepped="Diese Zeile behält den Abstand des Schritts darüber: zweiundzwanzig Punkt.",
)


# 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 main() -> None:
    language = _language.from_environment()
    words = _language.words_of(WORDS, language)
    lines = words.lines()

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

    font = hqf_pdf.Font.from_path(_out.font_path())
    print(
        f"font: {font.postscript_name} "
        f"({font.glyph_count} glyphs, {font.units_per_em} units per em)"
    )

    document = hqf_pdf.Document()
    document.set_license(_licence.licensed())  # no notice, so the text stands alone
    document.set_info("Title", words.title)
    handle = document.add_font(font)

    content = hqf_pdf.Content()
    content.begin_text()
    content.set_font(handle, 14.0)
    content.text_position(60.0, 760.0)
    for line in lines:
        content.show_glyphs(handle.glyphs(line))
        # Move down one line. The step is relative to the start of the previous one.
        content.text_position(0.0, -28.0)
    content.end_text()

    # A rule under the total, positioned from the measured width of the text, to prove
    # the metrics agree with what was drawn.
    width = handle.measure(lines[-1], 14.0)
    content.set_line_width(0.8)
    baseline = 760.0 - 28.0 * 5.0
    content.rect(60.0, baseline, width, 0.0)
    content.stroke()

    # The same kind of lines set with the text state: the leading carries each line
    # down, the second is spaced and the third narrowed, and a move that sets the
    # leading changes the step of every line after it.
    content.begin_text()
    content.set_font(handle, 11.0)
    content.set_leading(16.0)
    content.text_position(60.0, 560.0)
    content.show_glyphs(handle.glyphs(words.leading))
    content.next_line()
    content.set_character_spacing(1.5)
    content.show_glyphs(handle.glyphs(words.spaced))
    content.set_character_spacing(0.0)
    content.next_line()
    content.set_horizontal_scaling(80.0)
    content.show_glyphs(handle.glyphs(words.narrowed))
    content.set_horizontal_scaling(100.0)
    content.text_position_with_leading(0.0, -22.0)
    content.show_glyphs(handle.glyphs(words.stepped))
    content.end_text()

    # The rule under them is built as a stream of its own, then appended.
    rule = hqf_pdf.Content()
    rule.set_line_width(0.4)
    rule.move_to(60.0, 494.0)
    rule.line_to(535.0, 494.0)
    rule.stroke()
    content.append(rule)

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

    data = document.to_bytes()
    out.parent.mkdir(parents=True, exist_ok=True)
    out.write_bytes(data)

    print(f"wrote {out}: {len(data)} bytes")
    print(f"the last line measures {width:.1f} pt at 14 pt")

    handle.keep_all()
    whole = document.to_bytes()
    print(f"with every glyph kept: {len(whole)} bytes")


if __name__ == "__main__":
    main()
