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 | """A handbook that says how it would like to be opened, shown and printed.
The Python twin of the `write_reading_preferences` example in Rust. None of what
this document states changes a single mark on its pages. It changes what happens
the moment somebody double-clicks the file: which page comes up, how much of it
fills the window, what stands beside it, and what the print dialogue is already
set to when it opens.
Usage: python examples/write_reading_preferences.py [out.pdf] [font.ttf]
"""
from __future__ import annotations
import _licence
import _out
import hqf_pdf
# A4, in points.
SHEET = (595.276, 841.890)
# How far in from the edge of the sheet everything is set.
MARGIN = 64.0
# The colour the band across the head of a page is laid in.
BAND = (0.13, 0.29, 0.36)
# The address this document is published under, and the address its relative links are
# read against.
BASE = "https://example.org/handbook/"
# Where the underlined words of the link sit, and how wide they run.
LINK = (290.0, 104.0, 16.0)
def band():
"""The colour the band across the head of a page is laid in."""
return hqf_pdf.Rgb(BAND[0], BAND[1], BAND[2])
def head(content, font, heading):
"""Draw the band across the head of a page and the heading standing in it."""
content.save_state()
content.set_fill(band())
content.rect(0.0, SHEET[1] - 96.0, SHEET[0], 96.0)
content.fill()
content.set_fill(hqf_pdf.Rgb(1.0, 1.0, 1.0))
content.draw_text(font, 20.0, MARGIN, SHEET[1] - 58.0, heading)
content.restore_state()
def foot(content, font, number):
"""Draw the hairline and the line of small print that close a page."""
content.save_state()
content.set_stroke(hqf_pdf.Color.gray(0.82))
content.set_line_width(0.4)
content.move_to(MARGIN, 78.0)
content.line_to(SHEET[0] - MARGIN, 78.0)
content.stroke()
content.set_fill(hqf_pdf.Color.gray(0.5))
content.draw_text(font, 8.0, MARGIN, 62.0, "Field handbook")
content.draw_text(font, 8.0, SHEET[0] - MARGIN - 6.0, 62.0, number)
content.restore_state()
def lines(content, font, top, size, step, body):
"""Draw a run of lines down the page from `top`.
Hands back the baseline the next thing would go on.
"""
y = top
for line in body:
if line:
content.draw_text(font, size, MARGIN, y, line)
y -= step
return y
def setting(content, font, y, asked, does):
"""Draw a row of the settings table: what was asked for, and what it does."""
content.save_state()
content.set_fill(band())
content.draw_text(font, 9.5, MARGIN, y, asked)
content.set_fill(hqf_pdf.Color.gray(0.3))
content.draw_text(font, 9.5, MARGIN + 190.0, y, does)
content.set_stroke(hqf_pdf.Color.gray(0.85))
content.set_line_width(0.4)
content.move_to(MARGIN, y - 7.0)
content.line_to(SHEET[0] - MARGIN, y - 7.0)
content.stroke()
content.restore_state()
def cover(font):
"""The cover: the one page nobody is meant to land on."""
content = hqf_pdf.Content()
content.save_state()
content.set_fill(band())
content.rect(0.0, 0.0, SHEET[0], SHEET[1])
content.fill()
content.restore_state()
content.save_state()
content.set_fill(hqf_pdf.Rgb(1.0, 1.0, 1.0))
content.draw_text(font, 34.0, MARGIN, 540.0, "Field handbook")
content.set_fill(hqf_pdf.Rgb(0.72, 0.83, 0.86))
content.draw_text(
font,
13.0,
MARGIN,
504.0,
"How a document asks to be opened, shown and printed",
)
content.draw_text(font, 10.0, MARGIN, 120.0, "HQF Development")
content.restore_state()
content.save_state()
content.set_fill(hqf_pdf.Rgb(0.72, 0.83, 0.86))
lines(
content,
font,
420.0,
10.5,
16.0,
[
"This cover is page one, and it is not the page this document "
"opens on.",
"A reader that honours what the file states turns straight to the "
"contents,",
"shows the bookmarks beside it, and stands the cover on its own — "
"the way",
"a book falls open with its first page facing nothing.",
],
)
content.restore_state()
return content
def section_list(content, font):
"""Draw the list of sections, each under a hairline.
Hands back the baseline the next thing would go on.
"""
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.25))
y = SHEET[1] - 160.0
for item, page in [
("The cover", "1"),
("Contents", "2"),
("What this file asks of a reader", "3"),
("What it asks of a print dialogue", "4"),
]:
content.draw_text(font, 11.0, MARGIN, y, item)
content.draw_text(font, 11.0, SHEET[0] - MARGIN - 10.0, y, page)
content.save_state()
content.set_stroke(hqf_pdf.Color.gray(0.85))
content.set_line_width(0.4)
content.move_to(MARGIN, y - 7.0)
content.line_to(SHEET[0] - MARGIN, y - 7.0)
content.stroke()
content.restore_state()
y -= 34.0
content.restore_state()
return y
def terms(content, font):
"""Draw the base address, and the underlined words the link is laid over."""
y, width, _height = LINK
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.35))
content.draw_text(
font,
10.5,
MARGIN,
y + 72.0,
"The link below goes to terms.html, and to nothing else. It works "
"because this",
)
content.draw_text(
font,
10.5,
MARGIN,
y + 56.0,
"file states the address it was published under:",
)
content.set_fill(band())
content.draw_text(font, 10.5, MARGIN, y + 34.0, BASE)
content.set_fill(hqf_pdf.Rgb(0.10, 0.35, 0.65))
content.draw_text(font, 11.0, MARGIN, y + 4.0, "Read the full terms")
content.set_stroke(hqf_pdf.Rgb(0.10, 0.35, 0.65))
content.set_line_width(0.6)
content.move_to(MARGIN, y)
content.line_to(MARGIN + width, y)
content.stroke()
content.restore_state()
def contents(font):
"""The contents page: what the document opens on, and the one relative link."""
content = hqf_pdf.Content()
head(content, font, "Contents")
y = section_list(content, font)
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.35))
lines(
content,
font,
y - 66.0,
10.5,
16.0,
[
"This is the page the file opens on, and it opens showing what is "
"drawn on it",
"rather than the whole sheet: the words fill the window, not the "
"paper around",
"them. Turn to page three for the whole list of what this file asks "
"for, and to",
"page four for what it asks of a printer.",
],
)
content.restore_state()
terms(content, font)
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.45))
lines(
content,
font,
170.0,
9.5,
14.0,
[
"Move this file to another address and that link breaks — unless "
"the address",
"moves with it. That is all a base address is for: one line to "
"restate, instead",
"of every relative link in the document to rewrite.",
],
)
content.restore_state()
foot(content, font, "2")
return content
def on_screen(font):
"""What the file asks of the reader on the screen."""
content = hqf_pdf.Content()
head(content, font, "What this file asks of a reader")
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.35))
y = lines(
content,
font,
SHEET[1] - 148.0,
10.5,
16.0,
[
"Every line below is a wish, not an instruction. A reader whose "
"owner has said",
"otherwise does what its owner said, and a reader that has never "
"heard of one of",
"these entries ignores it and shows the file all the same.",
],
)
content.restore_state()
y -= 24.0
for asked, does in [
("Opens on", "the contents page, not the cover"),
("Shows", "what is drawn, filling the window"),
("Beside the page", "the list of bookmarks"),
("Arranges the pages", "two at a time, page one on its own"),
("Window", "resized to the first page, centred on the screen"),
("Title bar", "the title this file states, not its file name"),
("Leaving full screen", "back to the bookmarks"),
("Relative links", BASE),
]:
setting(content, font, y, asked, does)
y -= 44.0
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.45))
lines(
content,
font,
160.0,
9.5,
14.0,
[
"Nothing here is drawn on a page. Print this file and not one of "
"these lines",
"leaves a mark: they live in the catalogue, which is the first "
"thing a reader",
"reads and the last thing a printer cares about.",
],
)
content.restore_state()
foot(content, font, "3")
return content
def sheet_sketch(content, font, corner, caption):
"""Draw one sheet of the little diagram.
An outline, a band marking its head, and a caption under it.
"""
x, y = corner
content.save_state()
content.set_stroke(hqf_pdf.Color.gray(0.55))
content.set_line_width(0.8)
content.rect(x, y, 84.0, 118.0)
content.stroke()
content.set_fill(band())
content.rect(x + 8.0, y + 96.0, 68.0, 14.0)
content.fill()
content.set_fill(hqf_pdf.Color.gray(0.45))
content.draw_text(font, 8.0, x, y - 14.0, caption)
content.restore_state()
def long_edge(content, font):
"""Draw what turning a sheet on its long edge does.
The back of the sheet stands the same way up as its front.
"""
sheet_sketch(content, font, (MARGIN + 40.0, 216.0), "front: page two")
sheet_sketch(content, font, (MARGIN + 260.0, 216.0), "back: page three")
content.save_state()
content.set_stroke(hqf_pdf.Color.gray(0.55))
content.set_line_width(1.2)
content.move_to(MARGIN + 146.0, 275.0)
content.line_to(MARGIN + 246.0, 275.0)
content.line_to(MARGIN + 234.0, 283.0)
content.move_to(MARGIN + 246.0, 275.0)
content.line_to(MARGIN + 234.0, 267.0)
content.stroke()
content.set_fill(hqf_pdf.Color.gray(0.45))
content.draw_text(
font, 8.0, MARGIN + 148.0, 291.0, "turned on the long edge"
)
content.restore_state()
def at_the_printer(font):
"""What the file asks of the print dialogue."""
content = hqf_pdf.Content()
head(content, font, "What it asks of a print dialogue")
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.35))
y = lines(
content,
font,
SHEET[1] - 148.0,
10.5,
16.0,
[
"A print dialogue opens with something already filled in. A file "
"may say what,",
"which saves whoever prints it from setting the same four boxes "
"every time.",
],
)
content.restore_state()
y -= 24.0
for asked, does in [
("Scaling", "none: every page prints at the size it was drawn"),
("Sides", "both, the sheet turned on its long edge"),
("Paper tray", "chosen by the size of the page"),
("Copies", "two"),
("Pages", "two to four: the cover is not worth the toner"),
]:
setting(content, font, y, asked, does)
y -= 44.0
long_edge(content, font)
content.save_state()
content.set_fill(hqf_pdf.Color.gray(0.45))
lines(
content,
font,
160.0,
9.5,
14.0,
[
"Printing at actual size is the one that matters on a document "
"meant to be",
"measured: a form, a label sheet, a plan. A dialogue left to shrink "
"the page to",
"fit the paper is a dialogue that quietly makes every distance on "
"it wrong.",
],
)
content.restore_state()
foot(content, font, "4")
return content
def main() -> None:
out = _out.output_path("handbook")
document = hqf_pdf.Document()
document.set_license(_licence.licensed())
document.set_metadata(
hqf_pdf.Metadata(
title="Field handbook",
author="HQF Development",
subject="How a document asks to be opened, shown and printed",
)
)
font = document.add_font(hqf_pdf.Font.from_path(_out.font_path()))
first = hqf_pdf.Page(SHEET[0], SHEET[1])
first.set_content(cover(font))
document.add_page(first)
second = hqf_pdf.Page(SHEET[0], SHEET[1])
second.set_content(contents(font))
second.add_link(
hqf_pdf.Link.to_uri(MARGIN, LINK[0], LINK[1], LINK[2], "terms.html")
)
document.add_page(second)
third = hqf_pdf.Page(SHEET[0], SHEET[1])
third.set_content(on_screen(font))
document.add_page(third)
fourth = hqf_pdf.Page(SHEET[0], SHEET[1])
fourth.set_content(at_the_printer(font))
document.add_page(fourth)
for title, page in [
("The cover", 0),
("Contents", 1),
("What this file asks of a reader", 2),
("What it asks of a print dialogue", 3),
]:
document.add_bookmark(hqf_pdf.Bookmark(title, page))
# The document opens on its contents, filled with what is drawn there, with the
# bookmarks beside it and the cover standing on its own.
document.set_open_action(
hqf_pdf.OpenAction(1).view(hqf_pdf.PageFit.whole_drawing())
)
document.set_open_mode(hqf_pdf.OpenMode.Bookmarks)
document.set_page_layout(hqf_pdf.PageLayout.TwoPagesOddRight)
document.set_base_uri(BASE)
document.set_viewer_preferences(
hqf_pdf.ViewerPreferences()
.fit_window(True)
.center_window(True)
.show_title(True)
.after_full_screen(hqf_pdf.SidePanel.Bookmarks)
.print_scaling(hqf_pdf.PrintScaling.ActualSize)
.duplex(hqf_pdf.Duplex.LongEdge)
.tray_by_page_size(True)
.copies(2)
.print_run(1, 3)
)
written = document.write(out)
print(
f"wrote {out}: {written} bytes, {document.page_count} pages, and a "
f"catalogue that says how to open, show and print them"
)
if __name__ == "__main__":
main()
|