write_type_one_font.py

Die Python-Datei des Beispiels „Eine in einer Schrift von 1990 gesetzte Seite“. Eine Schrift in Adobes ältestem Format, wie sie eine Druckerei vor zwanzig Jahren gekauft hat und die heute fast nichts mehr liest, auf die Buchstaben gekürzt, die die Seite zeichnet. Jede Zahl, die die Seite über sie angibt, wurde aus der Schrift selbst gelesen.

Python 975 Zeilen

Wofür dieses Beispiel da ist

Eine Druckerei, die seit zwanzig Jahren Schriften kauft, besitzt eine Sammlung in Adobes erstem Schriftformat, gekauft und bezahlt, die fast nichts mehr liest. Sie zu ersetzen heißt, dieselben Schriften ein zweites Mal zu kaufen und hinzunehmen, dass ein Nachdruck nicht ganz der ursprünglichen Auflage entspricht. Diese Engine liest diese Sammlung, sodass sie weiter funktioniert, statt zum Grund zu werden, nichts zu ändern.

Was dieses Beispiel zeigt

  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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
"""Sets a page in a font read from a Type 1 program, and states what it says of itself.

The Python twin of the `write_type_one_font` example in Rust. Adobe's first font format
is not a table anybody can look a glyph up in: it is a PostScript program whose glyphs
are ciphered, and every measurement below is read out of it rather than written in by
hand. The program is carried into the file cut down to the glyphs the page draws.

The face is `HqfStroke`, HQF Development's own: a stroke face built by
`scripts/build_type1_sample.py`, committed beside the fonts the tests run against.

A second page reads the program glyph by glyph: its matrix, its slant, whether its
glyphs share one width, where its codes come from, and for each character of one line
the code that reaches it, the glyph's name and its advance. The same line is then set a
second time with a metrics file written over the program that widens every glyph by a
quarter, and the page says how long each line runs.

The face is measured on the text metrics file committed beside it, `HqfStroke.afm`, and
a third page sets one line twice: without kerning, then kerned on the pairs that file
states.

Both sets of words are held in `Words`, once per language, and `HQF_PDF_LANG` picks
which set is drawn.

Usage: python examples/write_type_one_font.py [out.pdf] [face.pfb [face.afm]]
       HQF_PDF_LANG=fr python examples/write_type_one_font.py

A face named without a metrics file is measured on its program alone.
"""

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 label over the three lines set in the face itself.
    shown: str
    # The three lines, in the capitals the face draws.
    samples: tuple[str, str, str]
    # The label over what the program states about itself.
    stated: str
    # The sentence those figures are put into.
    facts: str
    # The label over the weight.
    weight: str
    # The sentence the two weights are put into.
    weighed: str
    # The label over what of the program travels with the page.
    travels: str
    # How the program is cut down to what the page draws.
    carried: str
    # What such a font cannot do.
    caveat: str
    # The second page's heading.
    glyph_by_glyph: str
    # What the program states of its geometry; `{matrix}`, `{slant}`, `{pitch}` and
    # `{codes}` stand for them.
    geometry: str
    # Whether the glyphs share one width: yes, then no.
    pitch: tuple[str, str]
    # Where the codes come from: the published table, then the program's own, `{count}`
    # standing for how many codes it fills.
    codes: tuple[str, str]
    # One character of the line; `{character}`, `{code}`, `{name}` and `{advance}` stand
    # for them.
    reached_by: str
    # What the line set twice shows; `{program}` and `{metrics}` stand for how long it
    # runs each way.
    widened: str
    # The character that separates the whole part of a figure from its tenths.
    decimal: str
    # The label over the line set twice, on the third page.
    kerned: str
    # The line set twice: every pair the metrics file kerns.
    pairs: str
    # What the two settings of that line show.
    kerning: str


# Every pair the metrics file of the default face kerns, one after the other.
PAIRS = "AT AV AW AY FA LT LV LW LY PA TA VA WA YA"


# The page in English.
ENGLISH = Words(
    title="A page set in a Type 1 font",
    lead=(
        "The three lines below are drawn from a font program in Adobe's first format. "
        "It is not a font file with tables to look a glyph up in: it is a PostScript "
        "program, its glyphs ciphered one by one, and the library reads it to find out "
        "which code reaches each of them. Each glyph is set as wide as the text "
        "metrics file that comes with the program says, or, without one, as the "
        "program itself says. The program is then carried into this file cut down to "
        "the glyphs this page draws, and reading software draws them from those very "
        "bytes."
    ),
    shown="Drawn from the program",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "A STROKE FACE, READ AND CUT TO SIZE.",
    ),
    stated="What the program states about itself",
    facts=(
        "It calls itself %name% and holds %glyphs% glyphs, of which %reached% are "
        "reached by a code. Every glyph stands in one box, whose corners are %low% and "
        "%high%; a vertical stem is %stem% thousandths of an em thick, and an "
        "underline sits %underline% below the line. Not one of those figures is "
        "written into this page: each is read out of the program."
    ),
    weight="What it weighs",
    weighed=(
        "The program itself is %program% bytes on disk, and a page setting the three "
        "lines above in it weighs %document% bytes, the program cut down to the glyphs "
        "those lines draw. Both figures are measured by the program that created this "
        "page, not written in by hand."
    ),
    travels="What travels with it",
    carried=(
        "Like every other kind of font this library carries, a Type 1 program travels "
        "light: only the glyphs a page actually draws go into the file, so a page that "
        "says one word carries the letters of that word and not a whole alphabet. The "
        "format makes that a piece of work of its own: each glyph is ciphered on its "
        "own inside the program, so the library deciphers the program, rewrites its "
        "list of glyphs to hold only .notdef, the glyphs drawn and the glyphs an "
        "accented letter is built from, and ciphers it again. Each glyph kept is "
        "copied byte for byte, still under its own cipher, and the pieces of outline "
        "several glyphs share are kept whole. The font's description then names every "
        "glyph the file holds. On a face of a few dozen glyphs that saves little; on a "
        "large one it is most of the program: a face of sixty thousand bytes set in "
        "three words carries the glyphs of those three words and not the rest."
    ),
    caveat=(
        "A Type 1 font is a simple font: its codes are one byte, so at most 256 of its "
        "glyphs are reachable at a time and it never sets text down the page. Which "
        "code reaches which glyph is the program's own business — this file repeats "
        "the program's own names for them."
    ),
    glyph_by_glyph="The program, glyph by glyph",
    geometry="Its matrix is [{matrix}]; it slants {slant}°; {pitch}; {codes}.",
    pitch=(
        "every glyph has one width",
        "each glyph has a width of its own",
    ),
    codes=(
        "its codes follow the table the format publishes",
        "it spells out its own codes, {count} of them",
    ),
    reached_by=(
        "{character} is reached by code {code}, the glyph {name}, which advances "
        "{advance}"
    ),
    widened=(
        "The line above is set once from the widths the program states, and once with "
        "a metrics file written over it that widens every glyph by a quarter: at 15 "
        "points it runs {program} points, then {metrics}."
    ),
    decimal=".",
    kerned="Kerned from its metrics file",
    pairs=PAIRS,
    kerning=(
        "The same pairs twice: the first line is set without kerning, the second "
        "kerned on the pairs of the text metrics file the library reads beside the "
        "program. Each pair whose facing sides slant or stand open is brought "
        "together, by as much as a quarter of an em, and the move is written into the "
        "line itself, so every reader draws the letters where they were set."
    ),
)

# The page in French.
FRENCH = Words(
    title="Une page composée dans une police Type 1",
    lead=(
        "Les trois lignes ci-dessous sont tracées par un programme de police écrit "
        "dans le premier format d’Adobe. Ce n’est pas un fichier de police avec des "
        "tables où chercher un glyphe : c’est un programme PostScript, dont chaque "
        "glyphe est chiffré à part, et la bibliothèque le lit pour savoir quel code "
        "mène à chacun. Chaque glyphe prend la largeur que donne le fichier de "
        "métriques texte fourni avec le programme, ou, à défaut, celle que donne le "
        "programme lui-même. Le programme est ensuite incorporé à ce fichier, "
        "réduit aux seuls glyphes de cette page, et le logiciel de lecture les trace "
        "à partir de ces octets mêmes."
    ),
    shown="Tracé par le programme",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "UNE POLICE DE TRAITS, LUE PUIS ÉLAGUÉE.",
    ),
    stated="Ce que le programme dit de lui-même",
    facts=(
        "Il s’appelle %name% et contient %glyphs% glyphes, dont %reached% sont "
        "accessibles par un code. Tous les glyphes tiennent dans une même boîte, dont "
        "les coins sont %low% et %high% ; un fût vertical a une épaisseur de %stem% "
        "millièmes de cadratin, et le soulignement se place à %underline% sous la "
        "ligne. Aucun de ces chiffres n’est écrit à la main dans cette page : chacun "
        "est lu dans le programme."
    ),
    weight="Ce que cela pèse",
    weighed=(
        "Le programme lui-même pèse %program% octets sur le disque, et une page où les "
        "trois lignes ci-dessus sont composées dans cette police pèse %document% "
        "octets, y compris le programme réduit à leurs seuls glyphes. Les deux "
        "chiffres sont mesurés par le logiciel qui a créé cette page, pas écrits à la "
        "main."
    ),
    travels="Ce qui l’accompagne",
    carried=(
        "Comme toutes les autres sortes de polices que cette bibliothèque incorpore, "
        "un programme Type 1 voyage léger : seuls les glyphes réellement tracés sur "
        "une page entrent dans le fichier, si bien qu’une page qui contient un mot "
        "emporte les lettres de ce mot, et non un alphabet entier. Le format en fait "
        "un travail à part entière : chaque glyphe y est chiffré séparément, donc la "
        "bibliothèque déchiffre le programme, réécrit sa liste de glyphes pour n’y "
        "garder que .notdef, les glyphes tracés et ceux qui servent à construire une "
        "lettre accentuée, puis le rechiffre. Chaque glyphe gardé est recopié octet "
        "pour octet, toujours sous son propre chiffrement, et les morceaux de contour "
        "que plusieurs glyphes partagent sont conservés entiers. La description de la "
        "police nomme ensuite chaque glyphe que contient le fichier. Sur une police de "
        "quelques dizaines de glyphes, le gain est faible ; sur une grande, c’est "
        "l’essentiel du programme : une police de soixante mille octets employée pour "
        "trois mots n’emporte que les glyphes de ces trois mots."
    ),
    caveat=(
        "Une police Type 1 est une police simple : ses codes tiennent sur un octet, "
        "donc 256 de ses glyphes au plus sont accessibles à la fois, et elle ne "
        "compose jamais à la verticale. Quel code mène à quel glyphe relève du seul "
        "programme — ce fichier reprend les noms que le programme leur donne."
    ),
    glyph_by_glyph="Le programme, glyphe par glyphe",
    geometry="Sa matrice est [{matrix}] ; il penche de {slant}° ; {pitch} ; {codes}.",
    pitch=(
        "tous ses glyphes ont une même chasse",
        "chaque glyphe a sa propre chasse",
    ),
    codes=(
        "ses codes suivent la table que publie le format",
        "il détaille ses propres codes, au nombre de {count}",
    ),
    reached_by=(
        "{character} est atteint par le code {code}, le glyphe {name}, qui avance de "
        "{advance}"
    ),
    widened=(
        "La ligne ci-dessus est composée une fois avec les chasses que donne le "
        "programme, et une fois avec un fichier de métriques posé par-dessus qui "
        "élargit chaque glyphe d’un quart : à 15 points elle mesure {program} points, "
        "puis {metrics}."
    ),
    decimal=",",
    kerned="Crénage lu dans son fichier de métriques",
    pairs=PAIRS,
    kerning=(
        "Les mêmes paires, deux fois : la première ligne est composée sans crénage, la "
        "seconde avec le crénage des paires du fichier de métriques texte que la "
        "bibliothèque lit à côté du programme. Chaque paire dont les côtés en regard "
        "sont obliques ou ouverts est rapprochée, jusqu’à un quart de cadratin, et ce "
        "rapprochement est écrit dans la ligne elle-même : tout logiciel de lecture "
        "trace les lettres là où elles ont été placées."
    ),
)

# The page in Dutch.
DUTCH = Words(
    title="Een pagina gezet in een Type 1-lettertype",
    lead=(
        "De drie regels hieronder zijn getekend vanuit een lettertypeprogramma in het "
        "eerste formaat van Adobe. Het is geen lettertypebestand met tabellen om een "
        "glyph in op te zoeken: het is een PostScript-programma, met zijn glyphs een "
        "voor een versleuteld, en de bibliotheek leest het om te weten welke code "
        "elke glyph bereikt. Elke glyph wordt zo breed gezet als het metriekbestand "
        "bij het programma zegt, of, zonder zo’n bestand, als het programma zelf "
        "zegt. Het programma wordt dan in dit bestand ingesloten, ingekort tot de "
        "glyphs die deze pagina tekent, en leessoftware tekent ze vanuit precies die "
        "bytes."
    ),
    shown="Getekend vanuit het programma",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "EEN LIJNLETTER, GELEZEN EN OP MAAT GESNEDEN.",
    ),
    stated="Wat het programma over zichzelf zegt",
    facts=(
        "Het heet %name% en bevat %glyphs% glyphs, waarvan er %reached% door een code "
        "bereikt worden. Elke glyph staat in één kader, met als hoeken %low% en "
        "%high%; een verticale stam is %stem% duizendste em dik, en een onderstreping "
        "ligt %underline% onder de regel. Geen van die getallen staat op deze pagina "
        "geschreven: elk is uit het programma gelezen."
    ),
    weight="Wat het weegt",
    weighed=(
        "Het programma zelf is %program% bytes op schijf, en een pagina die er de "
        "drie regels hierboven in zet, weegt %document% bytes, met het programma "
        "ingekort tot de glyphs die die regels tekenen. Beide getallen zijn gemeten "
        "door het programma dat deze pagina maakte, niet met de hand geschreven."
    ),
    travels="Wat ermee meereist",
    carried=(
        "Zoals elke andere soort lettertype die deze bibliotheek insluit, reist een "
        "Type 1-programma licht: alleen de glyphs die een pagina echt tekent, gaan "
        "het bestand in, dus een pagina die één woord zegt, bevat de letters van dat "
        "woord en geen heel alfabet. Het formaat maakt daar een eigen karwei van: "
        "elke glyph is binnen het programma apart versleuteld, dus ontsleutelt de "
        "bibliotheek het programma, herschrijft de lijst van glyphs zodat die alleen "
        ".notdef bevat, de getekende glyphs en de glyphs waaruit een letter met "
        "accent wordt opgebouwd, en versleutelt het opnieuw. Elke behouden glyph "
        "wordt byte voor byte gekopieerd, nog onder zijn eigen versleuteling, en de "
        "stukken omtrek die meerdere glyphs delen blijven heel. De beschrijving van "
        "het lettertype noemt dan elke glyph die het bestand bevat. Bij een letter "
        "van een paar dozijn glyphs scheelt dat weinig; bij een grote is het het "
        "grootste deel van het programma: een letter van zestigduizend bytes, gezet "
        "in drie woorden, bevat de glyphs van die drie woorden en niet de rest."
    ),
    caveat=(
        "Een Type 1-lettertype is een eenvoudig lettertype: de codes zijn één byte, "
        "dus hoogstens 256 van de glyphs zijn tegelijk bereikbaar en het zet nooit "
        "tekst langs de pagina omlaag. Welke code welke glyph bereikt, is de zaak van "
        "het programma: dit bestand herhaalt de namen die het programma ze zelf geeft."
    ),
    glyph_by_glyph="Het programma, glyph voor glyph",
    geometry="De matrix is [{matrix}]; het helt {slant}°; {pitch}; {codes}.",
    pitch=(
        "elke glyph heeft één breedte",
        "elke glyph heeft een eigen breedte",
    ),
    codes=(
        "de codes volgen de tabel die het formaat publiceert",
        "het schrijft zijn eigen codes uit, {count} stuks",
    ),
    reached_by=(
        "{character} wordt bereikt met code {code}, de glyph {name}, die {advance} "
        "opschuift"
    ),
    widened=(
        "De regel hierboven is één keer gezet met de breedtes die het programma "
        "noemt, en één keer met een metriekbestand erover dat elke glyph een kwart "
        "breder maakt: op 15 punten loopt hij {program} punten, dan {metrics}."
    ),
    decimal=".",
    kerned="Gekerned uit het metriekbestand",
    pairs=PAIRS,
    kerning=(
        "Dezelfde paren twee keer: de eerste regel is gezet zonder kerning, de tweede "
        "met kerning op de paren van het metriekbestand dat de bibliotheek naast het "
        "programma leest. Elk paar waarvan de naar elkaar gekeerde zijden schuin "
        "lopen of open staan, wordt naar elkaar toe geschoven, tot een kwart em, en "
        "de verplaatsing wordt in de regel zelf geschreven, dus tekent elke lezer de "
        "letters waar ze gezet zijn."
    ),
)

# The page in Portuguese.
PORTUGUESE = Words(
    title="Uma página composta num tipo de letra Type 1",
    lead=(
        "As três linhas abaixo são desenhadas a partir de um programa de tipo de "
        "letra no primeiro formato da Adobe. Não é um ficheiro de tipo de letra com "
        "tabelas onde procurar um glifo: é um programa PostScript, com os glifos "
        "cifrados um a um, e a biblioteca lê-o para saber que código chega a cada um. "
        "Cada glifo é composto com a largura que diz o ficheiro de métricas que "
        "acompanha o programa ou, sem ele, a que diz o próprio programa. O programa é "
        "depois levado para este ficheiro reduzido aos glifos que esta página "
        "desenha, e o programa de leitura desenha-os a partir desses mesmos bytes."
    ),
    shown="Desenhado a partir do programa",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "UM TIPO DE TRAÇOS, LIDO E CORTADO À MEDIDA.",
    ),
    stated="O que o programa diz de si mesmo",
    facts=(
        "Chama-se %name% e tem %glyphs% glifos, dos quais %reached% são alcançados "
        "por um código. Cada glifo cabe numa caixa, cujos cantos são %low% e %high%; "
        "uma haste vertical tem %stem% milésimos de eme de espessura, e um sublinhado "
        "fica %underline% abaixo da linha. Nenhum destes números está escrito nesta "
        "página: cada um é lido do programa."
    ),
    weight="O que pesa",
    weighed=(
        "O próprio programa ocupa %program% bytes em disco, e uma página que compõe "
        "com ele as três linhas acima pesa %document% bytes, com o programa reduzido "
        "aos glifos que essas linhas desenham. Os dois números são medidos pelo "
        "programa que criou esta página, não escritos à mão."
    ),
    travels="O que viaja com ele",
    carried=(
        "Como qualquer outro tipo de letra que esta biblioteca leva, um programa Type "
        "1 viaja leve: só entram no ficheiro os glifos que uma página desenha de "
        "facto, por isso uma página que diz uma palavra leva as letras dessa palavra "
        "e não um alfabeto inteiro. O formato faz disso um trabalho próprio: cada "
        "glifo é cifrado à parte dentro do programa, por isso a biblioteca decifra o "
        "programa, reescreve a sua lista de glifos para conter só .notdef, os glifos "
        "desenhados e os glifos com que se constrói uma letra acentuada, e volta a "
        "cifrá-lo. Cada glifo mantido é copiado byte a byte, ainda sob a sua própria "
        "cifra, e os pedaços de contorno que vários glifos partilham são mantidos "
        "inteiros. A descrição do tipo de letra nomeia então cada glifo que o "
        "ficheiro contém. Num tipo de umas dezenas de glifos isso poupa pouco; num "
        "grande é quase todo o programa: um tipo de sessenta mil bytes composto em "
        "três palavras leva os glifos dessas três palavras e não o resto."
    ),
    caveat=(
        "Um tipo de letra Type 1 é um tipo simples: os seus códigos são de um byte, "
        "por isso no máximo 256 dos seus glifos são alcançáveis de cada vez e nunca "
        "compõe texto para baixo. Que código chega a que glifo é assunto do programa: "
        "este ficheiro repete os nomes que o próprio programa lhes dá."
    ),
    glyph_by_glyph="O programa, glifo a glifo",
    geometry="A sua matriz é [{matrix}]; inclina {slant}°; {pitch}; {codes}.",
    pitch=(
        "todos os glifos têm a mesma largura",
        "cada glifo tem uma largura própria",
    ),
    codes=(
        "os seus códigos seguem a tabela que o formato publica",
        "detalha os seus próprios códigos, {count} ao todo",
    ),
    reached_by=(
        "{character} é alcançado pelo código {code}, o glifo {name}, que avança "
        "{advance}"
    ),
    widened=(
        "A linha acima é composta uma vez com as larguras que o programa indica, e "
        "outra com um ficheiro de métricas escrito por cima que alarga cada glifo em "
        "um quarto: a 15 pontos mede {program} pontos, depois {metrics}."
    ),
    decimal=".",
    kerned="Com crenagem do seu ficheiro de métricas",
    pairs=PAIRS,
    kerning=(
        "Os mesmos pares duas vezes: a primeira linha é composta sem crenagem, a "
        "segunda com os pares do ficheiro de métricas que a biblioteca lê ao lado do "
        "programa. Cada par cujos lados virados um para o outro se inclinam ou ficam "
        "abertos é aproximado, até um quarto de eme, e o movimento é escrito na "
        "própria linha, por isso cada leitor desenha as letras onde foram compostas."
    ),
)

# The page in Italian.
ITALIAN = Words(
    title="Una pagina composta in un font Type 1",
    lead=(
        "Le tre righe qui sotto sono disegnate da un programma di font nel primo "
        "formato di Adobe. Non è un file di font con tabelle in cui cercare un glifo: "
        "è un programma PostScript, con i glifi cifrati uno per uno, e la libreria lo "
        "legge per sapere quale codice raggiunge ciascuno. Ogni glifo è composto "
        "largo quanto dice il file di metriche che accompagna il programma o, senza, "
        "quanto dice il programma stesso. Il programma è poi portato in questo file "
        "ridotto ai glifi che questa pagina disegna, e il programma di lettura li "
        "disegna da quegli stessi byte."
    ),
    shown="Disegnato dal programma",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "UN CARATTERE A TRATTI, LETTO E RIDOTTO.",
    ),
    stated="Cosa dice di sé il programma",
    facts=(
        "Si chiama %name% e contiene %glyphs% glifi, di cui %reached% raggiunti da un "
        "codice. Ogni glifo sta in un riquadro, i cui angoli sono %low% e %high%; "
        "un’asta verticale è spessa %stem% millesimi di em, e una sottolineatura sta "
        "%underline% sotto la riga. Nessuna di queste cifre è scritta in questa "
        "pagina: ciascuna è letta dal programma."
    ),
    weight="Quanto pesa",
    weighed=(
        "Il programma stesso occupa %program% byte su disco, e una pagina che vi "
        "compone le tre righe qui sopra pesa %document% byte, con il programma "
        "ridotto ai glifi che quelle righe disegnano. Entrambe le cifre sono misurate "
        "dal programma che ha creato questa pagina, non scritte a mano."
    ),
    travels="Cosa viaggia con esso",
    carried=(
        "Come ogni altro tipo di font che questa libreria porta, un programma Type 1 "
        "viaggia leggero: nel file entrano solo i glifi che una pagina disegna "
        "davvero, così una pagina che dice una parola porta le lettere di quella "
        "parola e non un alfabeto intero. Il formato ne fa un lavoro a sé: ogni glifo "
        "è cifrato separatamente dentro il programma, così la libreria decifra il "
        "programma, riscrive il suo elenco di glifi perché contenga solo .notdef, i "
        "glifi disegnati e quelli da cui si costruisce una lettera accentata, e lo "
        "cifra di nuovo. Ogni glifo conservato è copiato byte per byte, ancora sotto "
        "il proprio cifrario, e i pezzi di contorno che più glifi condividono sono "
        "conservati interi. La descrizione del font nomina allora ogni glifo che il "
        "file contiene. Su un carattere di qualche decina di glifi si risparmia poco; "
        "su uno grande è quasi tutto il programma: un carattere di sessantamila byte "
        "composto in tre parole porta i glifi di quelle tre parole e non il resto."
    ),
    caveat=(
        "Un font Type 1 è un font semplice: i suoi codici sono di un byte, così al "
        "massimo 256 dei suoi glifi sono raggiungibili insieme e non compone mai "
        "testo verso il basso. Quale codice raggiunge quale glifo è affare del "
        "programma: questo file ripete i nomi che il programma stesso dà loro."
    ),
    glyph_by_glyph="Il programma, glifo per glifo",
    geometry="La sua matrice è [{matrix}]; inclina di {slant}°; {pitch}; {codes}.",
    pitch=(
        "ogni glifo ha la stessa larghezza",
        "ogni glifo ha una larghezza propria",
    ),
    codes=(
        "i suoi codici seguono la tabella che il formato pubblica",
        "elenca i propri codici, {count} in tutto",
    ),
    reached_by=(
        "{character} è raggiunto dal codice {code}, il glifo {name}, che avanza di "
        "{advance}"
    ),
    widened=(
        "La riga qui sopra è composta una volta con le larghezze che indica il "
        "programma, e una volta con un file di metriche scritto sopra che allarga "
        "ogni glifo di un quarto: a 15 punti misura {program} punti, poi {metrics}."
    ),
    decimal=".",
    kerned="Con la crenatura del suo file di metriche",
    pairs=PAIRS,
    kerning=(
        "Le stesse coppie due volte: la prima riga è composta senza crenatura, la "
        "seconda con le coppie del file di metriche che la libreria legge accanto al "
        "programma. Ogni coppia i cui lati affacciati si inclinano o restano aperti è "
        "avvicinata, fino a un quarto di em, e lo spostamento è scritto nella riga "
        "stessa, così ogni programma di lettura disegna le lettere dove sono state "
        "composte."
    ),
)

# The page in Spanish.
SPANISH = Words(
    title="Una página compuesta en una fuente Type 1",
    lead=(
        "Las tres líneas de abajo se dibujan con un programa de fuente en el primer "
        "formato de Adobe. No es un archivo de fuente con tablas en que buscar un "
        "glifo: es un programa PostScript, con sus glifos cifrados uno a uno, y la "
        "biblioteca lo lee para saber qué código lleva a cada uno. Cada glifo se "
        "compone con el ancho que dice el archivo de métricas que acompaña al "
        "programa o, sin él, el que dice el propio programa. Luego el programa se "
        "lleva a este archivo recortado a los glifos que dibuja esta página, y el "
        "programa de lectura los dibuja con esos mismos bytes."
    ),
    shown="Dibujado con el programa",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "UNA FUENTE DE TRAZOS, LEÍDA Y RECORTADA.",
    ),
    stated="Lo que el programa dice de sí mismo",
    facts=(
        "Se llama %name% y tiene %glyphs% glifos, de los que %reached% son accesibles "
        "por un código. Cada glifo cabe en una caja, cuyas esquinas son %low% y "
        "%high%; un fuste vertical tiene %stem% milésimas de eme de grosor, y un "
        "subrayado va %underline% bajo la línea. Ninguna de esas cifras se escribe en "
        "esta página: cada una se lee del programa."
    ),
    weight="Lo que pesa",
    weighed=(
        "El programa mismo ocupa %program% bytes en disco, y una página que compone "
        "con él las tres líneas de arriba pesa %document% bytes, con el programa "
        "recortado a los glifos que dibujan esas líneas. Las dos cifras las mide el "
        "programa que creó esta página, no se escriben a mano."
    ),
    travels="Lo que viaja con ella",
    carried=(
        "Como cualquier otro tipo de fuente que lleva esta biblioteca, un programa "
        "Type 1 viaja ligero: solo los glifos que una página dibuja de verdad entran "
        "en el archivo, así que una página que dice una palabra lleva las letras de "
        "esa palabra y no un alfabeto entero. El formato hace de eso un trabajo "
        "propio: cada glifo va cifrado por separado dentro del programa, así que la "
        "biblioteca descifra el programa, reescribe su lista de glifos para que solo "
        "tenga .notdef, los glifos dibujados y los glifos con que se construye una "
        "letra acentuada, y lo vuelve a cifrar. Cada glifo conservado se copia byte a "
        "byte, aún bajo su propio cifrado, y los trozos de contorno que comparten "
        "varios glifos se conservan enteros. La descripción de la fuente nombra "
        "entonces cada glifo que tiene el archivo. En una fuente de unas decenas de "
        "glifos eso ahorra poco; en una grande es casi todo el programa: una fuente "
        "de sesenta mil bytes compuesta en tres palabras lleva los glifos de esas "
        "tres palabras y no el resto."
    ),
    caveat=(
        "Una fuente Type 1 es una fuente simple: sus códigos son de un byte, así que "
        "como mucho 256 de sus glifos son accesibles a la vez y nunca compone texto "
        "hacia abajo. Qué código lleva a qué glifo es cosa del programa: este archivo "
        "repite los nombres que el propio programa les da."
    ),
    glyph_by_glyph="El programa, glifo a glifo",
    geometry="Su matriz es [{matrix}]; se inclina {slant}°; {pitch}; {codes}.",
    pitch=(
        "todos los glifos tienen un mismo ancho",
        "cada glifo tiene su propio ancho",
    ),
    codes=(
        "sus códigos siguen la tabla que publica el formato",
        "detalla sus propios códigos, {count} en total",
    ),
    reached_by=(
        "{character} se alcanza con el código {code}, el glifo {name}, que avanza "
        "{advance}"
    ),
    widened=(
        "La línea de arriba se compone una vez con los anchos que indica el programa, "
        "y otra con un archivo de métricas escrito encima que ensancha cada glifo un "
        "cuarto: a 15 puntos mide {program} puntos, luego {metrics}."
    ),
    decimal=".",
    kerned="Con interletraje de su archivo de métricas",
    pairs=PAIRS,
    kerning=(
        "Los mismos pares dos veces: la primera línea se compone sin interletraje, la "
        "segunda con los pares del archivo de métricas que la biblioteca lee junto al "
        "programa. Cada par cuyos lados enfrentados se inclinan o quedan abiertos se "
        "acerca, hasta un cuarto de eme, y el movimiento se escribe en la propia "
        "línea, así que todo programa de lectura dibuja las letras donde se "
        "compusieron."
    ),
)

# The page in German.
GERMAN = Words(
    title="Eine in einer Type-1-Schrift gesetzte Seite",
    lead=(
        "Die drei Zeilen unten sind aus einem Schriftprogramm in Adobes erstem Format "
        "gezeichnet. Es ist keine Schriftdatei mit Tabellen, in denen man einen "
        "Glyphen nachschlägt: Es ist ein PostScript-Programm, seine Glyphen einzeln "
        "verschlüsselt, und die Bibliothek liest es, um herauszufinden, welcher Code "
        "jeden von ihnen erreicht. Jeder Glyph wird so breit gesetzt, wie die zum "
        "Programm gehörende Metrikdatei sagt, oder, ohne sie, wie das Programm selbst "
        "sagt. Das Programm wird dann, auf die Glyphen dieser Seite gekürzt, in diese "
        "Datei mitgenommen, und die Lesesoftware zeichnet sie aus genau diesen Bytes."
    ),
    shown="Aus dem Programm gezeichnet",
    samples=(
        "HQF DEVELOPMENT",
        "ABCDEFGHIJKLM NOPQRSTUVWXYZ 0123456789",
        "EIN STRICHSCHNITT, GELESEN UND ZUGESCHNITTEN.",
    ),
    stated="Was das Programm über sich angibt",
    facts=(
        "Es nennt sich %name% und enthält %glyphs% Glyphen, von denen %reached% über "
        "einen Code erreichbar sind. Jeder Glyph steht in einem Rahmen mit den Ecken "
        "%low% und %high%; ein senkrechter Stamm ist %stem% Tausendstel Geviert dick, "
        "und eine Unterstreichung liegt %underline% unter der Zeile. Keine dieser "
        "Zahlen ist in diese Seite geschrieben: Jede wird aus dem Programm gelesen."
    ),
    weight="Was es wiegt",
    weighed=(
        "Das Programm selbst hat %program% Bytes auf der Platte, und eine Seite, die "
        "die drei Zeilen oben darin setzt, wiegt %document% Bytes, das Programm auf "
        "die Glyphen dieser Zeilen gekürzt. Beide Zahlen misst das Programm, das "
        "diese Seite erstellt hat, keine ist von Hand eingetragen."
    ),
    travels="Was mitreist",
    carried=(
        "Wie jede andere Schriftart, die diese Bibliothek mitführt, reist ein "
        "Type-1-Programm mit leichtem Gepäck: Nur die Glyphen, die eine Seite "
        "tatsächlich zeichnet, kommen in die Datei, also trägt eine Seite, die ein "
        "Wort sagt, die Buchstaben dieses Worts und nicht ein ganzes Alphabet. Das "
        "Format macht daraus eine eigene Arbeit: Jeder Glyph ist im Programm einzeln "
        "verschlüsselt, also entschlüsselt die Bibliothek das Programm, schreibt "
        "seine Glyphenliste so um, dass sie nur .notdef, die gezeichneten Glyphen und "
        "die Glyphen enthält, aus denen ein Buchstabe mit Akzent gebaut ist, und "
        "verschlüsselt es wieder. Jeder behaltene Glyph wird Byte für Byte kopiert, "
        "noch unter seiner eigenen Verschlüsselung, und die Konturteile, die mehrere "
        "Glyphen teilen, bleiben ganz. Die Beschreibung der Schrift nennt dann jeden "
        "Glyphen, den die Datei enthält. Bei einem Schnitt von ein paar Dutzend "
        "Glyphen spart das wenig; bei einem großen ist es der größte Teil des "
        "Programms: Ein Schnitt von sechzigtausend Bytes, in drei Wörtern gesetzt, "
        "trägt die Glyphen dieser drei Wörter und nicht den Rest."
    ),
    caveat=(
        "Eine Type-1-Schrift ist eine einfache Schrift: Ihre Codes sind ein Byte, "
        "also sind höchstens 256 ihrer Glyphen zugleich erreichbar, und sie setzt nie "
        "senkrecht. Welcher Code welchen Glyphen erreicht, ist Sache des Programms — "
        "diese Datei wiederholt die eigenen Namen, die das Programm ihnen gibt."
    ),
    glyph_by_glyph="Das Programm, Glyph für Glyph",
    geometry="Seine Matrix ist [{matrix}]; es neigt sich {slant}°; {pitch}; {codes}.",
    pitch=(
        "alle Glyphen haben dieselbe Breite",
        "jeder Glyph hat seine eigene Breite",
    ),
    codes=(
        "seine Codes folgen der Tabelle, die das Format veröffentlicht",
        "es schreibt seine eigenen Codes aus, {count} davon",
    ),
    reached_by=(
        "{character} erreicht über Code {code} den Glyphen {name}, der um {advance} "
        "vorrückt"
    ),
    widened=(
        "Die Zeile oben ist einmal nach den Breiten gesetzt, die das Programm angibt, "
        "und einmal mit einer darübergelegten Metrikdatei, die jeden Glyphen um ein "
        "Viertel verbreitert: bei 15 Punkt läuft sie {program} Punkt, dann {metrics}."
    ),
    decimal=".",
    kerned="Nach seiner Metrikdatei unterschnitten",
    pairs=PAIRS,
    kerning=(
        "Dieselben Paare zweimal: Die erste Zeile ist ohne Unterschneidung gesetzt, "
        "die zweite nach den Paaren der Metrikdatei unterschnitten, die die "
        "Bibliothek neben dem Programm liest. Jedes Paar, dessen einander zugewandte "
        "Seiten schräg stehen oder offen sind, rückt zusammen, um bis zu ein Viertel "
        "Geviert, und die Verschiebung wird in die Zeile selbst geschrieben, also "
        "zeichnet jeder Leser die Buchstaben dort, wo sie gesetzt wurden."
    ),
)

# 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 left edge of everything on the page.
LEFT = 72.0

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

# The Type 1 program the page is set in when the caller names none.
DEFAULT_FACE = _out.ROOT / "crates" / "hqf-pdf" / "tests" / "fonts" / "HqfStroke.pfb"

# The text metrics file of the default face, committed beside it.
DEFAULT_METRICS = DEFAULT_FACE.with_suffix(".afm")


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 spaced(count: int) -> str:
    """A count with a no-break space between its thousands."""
    digits = str(count)
    out = []
    for index, digit in enumerate(digits):
        if index > 0 and (len(digits) - index) % 3 == 0:
            out.append("\u00a0")
        out.append(digit)
    return "".join(out)


def reached(face: hqf_pdf.Type1Font) -> int:
    """How many of the program's glyphs a code reaches."""
    return sum(1 for code in range(256) if face.character_at(code) is not None)


def corners(face: hqf_pdf.Type1Font) -> tuple[str, str]:
    """The two corners of the box a face states, as the page writes them."""
    llx, lly, urx, ury = face.bbox
    return f"{llx:g} · {lly:g}", f"{urx:g} · {ury:g}"


def weighed(words: Words, face: hqf_pdf.Type1Font) -> int:
    """What a page holding nothing but the three samples weighs.

    It is measured on a page of its own: a page that states its own weight never
    settles, because writing the figure changes the figure.
    """
    doc = hqf_pdf.Document()
    doc.set_license(_licence.licensed())
    handle = doc.add_type1_font(face)

    content = hqf_pdf.Content()
    top = 760.0
    for sample in words.samples:
        top = block(content, handle, 15.0, top, sample) - 10.0

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


def build(
    words: Words,
    face: hqf_pdf.Type1Font,
    bare: hqf_pdf.Type1Font,
    prose: Path,
    program: int,
    document: int,
) -> bytes:
    """Draws the whole page."""
    doc = hqf_pdf.Document()
    doc.set_license(_licence.licensed())
    text = doc.add_font(hqf_pdf.Font.from_path(prose))
    drawn = doc.add_type1_font(face)

    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) - 24.0

    top = block(content, text, 12.0, top, words.shown) - 14.0
    for sample in words.samples:
        top = block(content, drawn, 15.0, top, sample) - 10.0

    top -= 14.0
    top = block(content, text, 12.0, top, words.stated) - 10.0
    low, high = corners(face)
    stem = "—" if face.stem_v is None else f"{face.stem_v:g}"
    underline = "—" if face.underline is None else f"{-face.underline[0]:g}"
    stated = (
        words.facts.replace("%name%", face.postscript_name)
        .replace("%glyphs%", str(len(face.glyph_names)))
        .replace("%reached%", str(reached(face)))
        .replace("%low%", low)
        .replace("%high%", high)
        .replace("%stem%", stem)
        .replace("%underline%", underline)
    )
    top = block(content, text, 10.0, top, stated) - 20.0

    top = block(content, text, 12.0, top, words.weight) - 10.0
    said = words.weighed.replace("%program%", spaced(program)).replace(
        "%document%", spaced(document)
    )
    top = block(content, text, 10.0, top, said) - 20.0

    top = block(content, text, 12.0, top, words.travels) - 10.0
    top = block(content, text, 10.0, top, words.carried) - 20.0

    content.set_fill(hqf_pdf.Rgb.gray(0.35))
    block(content, text, 9.0, top, words.caveat)

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    doc.add_page(page)
    doc.add_page(second_page(doc, text, bare, words))

    kerned = drawn.kerning()
    content = hqf_pdf.Content()
    top = 760.0
    top = block(content, text, 12.0, top, words.kerned) - 14.0
    top = block(content, drawn, 15.0, top, words.pairs) - 10.0
    top = block(content, kerned, 15.0, top, words.pairs) - 20.0
    block(content, text, 10.0, top, words.kerning)

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


# The line the second page reads glyph by glyph and sets twice, drawn in the capitals
# and figures the face holds.
LINE = "HQF 2038"


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 widening(face: hqf_pdf.Type1Font) -> str:
    """A metrics file for `face` that states every glyph a quarter wider than the
    program does."""
    lines = [
        f"C -1 ; WX {(face.advance_of(name) or 0.0) * 1.25:.0f} ; N {name} ;"
        for name in face.glyph_names
    ]
    return (
        f"StartFontMetrics 4.1\nFontName {face.postscript_name}\n"
        f"StartCharMetrics {len(lines)}\n" + "\n".join(lines)
        + "\nEndCharMetrics\nEndFontMetrics\n"
    )


def second_page(doc, text, face, words: Words) -> hqf_pdf.Page:
    """The second page: the program's geometry, the line read glyph by glyph, and the
    line set twice, the second time under a metrics file that widens it."""
    content = hqf_pdf.Content()
    top = 760.0
    top = block(content, text, 18.0, top, words.glyph_by_glyph) - 14.0

    matrix = " ".join(number(entry) for entry in face.matrix)
    if face.names_the_published_encoding:
        codes = words.codes[0]
    else:
        codes = words.codes[1].replace("{count}", str(len(face.encoding or {})))
    geometry = (
        words.geometry.replace("{matrix}", matrix)
        .replace("{slant}", number(face.italic_angle))
        .replace("{pitch}", words.pitch[0 if face.fixed_pitch else 1])
        .replace("{codes}", codes)
    )
    top = block(content, text, 10.0, top, geometry) - 12.0

    for character in LINE.replace(" ", ""):
        code = face.code_of(character)
        if code is None:
            continue
        name = face.name_at(code) or ""
        advance = face.advance_of(name)
        if advance is None:
            advance = face.advance_at(code)
        said = (
            words.reached_by.replace("{character}", character)
            .replace("{code}", str(code))
            .replace("{name}", name)
            .replace("{advance}", number(advance))
        )
        top = block(content, text, 9.5, top, said) - 2.0

    widened = face.with_metrics(widening(face).encode())
    handles = [doc.add_type1_font(face), doc.add_type1_font(widened)]
    top -= 16.0
    runs = []
    for handle in handles:
        top = block(content, handle, 15.0, top, LINE) - 8.0
        runs.append(f"{handle.measure(LINE, 15.0):.1f}".replace(".", words.decimal))
    said = words.widened.replace("{program}", runs[0]).replace("{metrics}", runs[1])
    block(content, text, 10.0, top - 8.0, said)

    page = hqf_pdf.Page.a4()
    page.set_content(content)
    return page


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("type_one_font.pdf", language)).stem
    )
    argv = __import__("sys").argv
    if len(argv) > 2:
        face_path = _out.font_path()
        metrics_path = Path(argv[3]) if len(argv) > 3 else None
    else:
        face_path, metrics_path = DEFAULT_FACE, DEFAULT_METRICS

    data = Path(face_path).read_bytes()
    bare = hqf_pdf.Type1Font.from_bytes(data)
    face = bare
    if metrics_path is not None:
        face = bare.with_metrics(metrics_path.read_bytes())

    document = weighed(words, face)
    drawn = build(words, face, bare, _out.DEFAULT_FONT, len(data), document)

    Path(out).parent.mkdir(parents=True, exist_ok=True)
    Path(out).write_bytes(drawn)
    print(f"wrote {out}: {len(drawn)} bytes")


if __name__ == "__main__":
    main()