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 | """Sets one justified paragraph in a narrow column twice: above without its soft
hyphens, below with them, so the breaks and the hyphens they draw can be seen
against the loose lines above.
The Python twin of the `write_soft_hyphen` example in Rust. The text carries soft
hyphens (U+00AD) inside its long words and a no-break space (U+00A0) inside the
figure "1 200 EUR". The upper setting is the same text with the soft hyphens taken
out, so only the breaking differs between the two; the no-break space holds the
figure together in both.
The paragraph and the two headings are held in `Words`, once per language, and
`HQF_PDF_LANG` picks which set is drawn. Every language has to carry its own soft
hyphens, in words its own spelling allows to break: a paragraph translated without
them would show nothing.
Usage: python examples/write_soft_hyphen.py [out.pdf] [font.ttf]
HQF_PDF_LANG=fr python examples/write_soft_hyphen.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."""
# What stands over the setting whose soft hyphens have been taken out.
without: str
# What stands over the setting that keeps them.
with_: str
# The paragraph, with soft hyphens marking where its long words may break and a
# no-break space inside the figure so it never falls across two lines. Each language
# marks the breaks its own spelling allows.
hyphenated: str
# The page in English.
ENGLISH = Words(
without="Without soft hyphens: justify opens the lines to fill the column.",
with_="With soft hyphens: the long words break, each break drawing a hyphen.",
hyphenated=(
"Our international subscription, billed at "
"1 200 EUR, covers unlimited documents, priority "
"support and quarterly statements."
),
)
# The page in French.
FRENCH = Words(
without=(
("Sans césures conditionnelles : la justification écarte les mots pour remplir"
" la colonne.")
),
with_=(
("Avec césures conditionnelles : les mots longs se coupent, chaque coupure "
"ajoutant un trait d’union.")
),
hyphenated=(
("Notre abonnement international, facturé 1 200 EUR, comprend un nombre "
"illimité de documents, une assistance prioritaire et des relevés "
"trimestriels.")
),
)
# The page in Dutch.
DUTCH = Words(
without="Zonder zachte afbreekstreepjes: uitvullen trekt de regels open om de kolom te vullen.",
with_="Met zachte afbreekstreepjes: de lange woorden breken, en elke breuk tekent een streepje.",
hyphenated=(
"Ons internationale abonnement, gefactureerd tegen EUR 1.200, omvat "
"onbeperkte documenten, voorrangsondersteuning en driemaandelijkse "
"overzichten."
),
)
# The page in Portuguese.
PORTUGUESE = Words(
without="Sem hífenes condicionais: justificar abre as linhas para encher a coluna.",
with_="Com hífenes condicionais: as palavras longas partem-se, e cada corte desenha um hífen.",
hyphenated=(
"A nossa assinatura internacional, faturada a 1 200 EUR, cobre documentos "
"ilimitados, assistência prioritária e extratos trimestrais."
),
)
# The page in Italian.
ITALIAN = Words(
without="Senza trattini facoltativi: la giustificazione allarga le righe per riempire la colonna.",
with_="Con trattini facoltativi: le parole lunghe si spezzano, e ogni taglio disegna un trattino.",
hyphenated=(
"Il nostro abbonamento internazionale, fatturato a 1.200 EUR, copre "
"documenti illimitati, assistenza prioritaria ed estratti trimestrali."
),
)
# The page in Spanish.
SPANISH = Words(
without="Sin guiones condicionales: justificar abre las líneas para llenar la columna.",
with_="Con guiones condicionales: las palabras largas se cortan, y cada corte dibuja un guion.",
hyphenated=(
"Nuestra suscripción internacional, facturada a 1 200 EUR, cubre "
"documentos ilimitados, asistencia prioritaria y extractos trimestrales."
),
)
# The page in German.
GERMAN = Words(
without="Ohne weiche Trennstriche: Der Blocksatz öffnet die Zeilen, um die Spalte zu füllen.",
with_="Mit weichen Trennstrichen: Die langen Wörter brechen, jeder Bruch zeichnet einen Bindestrich.",
hyphenated=(
"Unser internationales Abonnement, abgerechnet mit 1 200 EUR, umfasst "
"unbegrenzte Dokumente, bevorzugten Support und vierteljährliche "
"Kontoauszüge."
),
)
# 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 column both settings are broken to, narrow enough that the long words reach its
# edge.
COLUMN = 150.0
# The left edge of both settings, stacked one above the other.
X = 80.0
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("soft_hyphen.pdf", language)).stem)
document = hqf_pdf.Document()
document.set_license(_licence.licensed())
handle = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))
content = hqf_pdf.Content()
# The upper setting is the same text with its soft hyphens removed, so the two
# panels differ only in whether the words may break.
plain = words.hyphenated.replace("", "")
for top, label, paragraph in (
(760.0, words.without, plain),
(600.0, words.with_, words.hyphenated),
):
heading = hqf_pdf.TextFlow(handle, 8.0)
heading_lines = heading.break_lines(label, 400.0)
heading.draw(content, heading_lines, X, top, 400.0)
text_top = top - 20.0
flow = hqf_pdf.TextFlow(handle, 11.0, leading=15.0, align=hqf_pdf.Align.Justify)
lines = flow.break_lines(paragraph, COLUMN)
flow.draw(content, lines, X, text_top, COLUMN)
# The column's edge, so the lines can be read against it.
height = flow.height(lines)
content.save_state()
content.set_stroke(hqf_pdf.Rgb.gray(0.8))
content.set_line_width(0.5)
content.rect(X, text_top - height, COLUMN, height)
content.stroke()
content.restore_state()
page = hqf_pdf.Page.a4()
page.set_content(content)
document.add_page(page)
written = document.write(out)
print(f"wrote {out}: {written} bytes")
if __name__ == "__main__":
main()
|