python-pptx2 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pptx2/__init__.py +9 -3
- pptx2/design/blocks.py +162 -3
- pptx2/lint.py +4 -3
- pptx2/render.py +17 -1
- pptx2/skill/SKILL.md +19 -5
- pptx2/skill/references/slide-design.md +550 -495
- {python_pptx2-3.1.0.dist-info → python_pptx2-3.2.0.dist-info}/METADATA +1 -1
- {python_pptx2-3.1.0.dist-info → python_pptx2-3.2.0.dist-info}/RECORD +12 -12
- {python_pptx2-3.1.0.dist-info → python_pptx2-3.2.0.dist-info}/WHEEL +0 -0
- {python_pptx2-3.1.0.dist-info → python_pptx2-3.2.0.dist-info}/entry_points.txt +0 -0
- {python_pptx2-3.1.0.dist-info → python_pptx2-3.2.0.dist-info}/licenses/LICENSE +0 -0
- {python_pptx2-3.1.0.dist-info → python_pptx2-3.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,495 +1,550 @@
|
|
|
1
|
-
# Slide design: making a generated deck look designed
|
|
2
|
-
|
|
3
|
-
`space-aware-authoring.md` makes a deck *physically* correct — nothing
|
|
4
|
-
overflows, nothing falls off the slide. This file is about the other
|
|
5
|
-
half: a deck that looks like a designer laid it out. Everything here is
|
|
6
|
-
built from the public API (`BBox`, `add_text`, `add_card`, `add_bullets`,
|
|
7
|
-
`add_picture_fit`, `PALETTES`, `pptx2.diagrams`, `render_contact_sheet`),
|
|
8
|
-
so the code samples run as written.
|
|
9
|
-
|
|
10
|
-
Read this when the deck will be *seen* — a lesson, a talk, a pitch — and
|
|
11
|
-
not only parsed.
|
|
12
|
-
|
|
13
|
-
## The one-paragraph brief
|
|
14
|
-
|
|
15
|
-
A slide that looks designed has **one idea**, **one focal point**, **one
|
|
16
|
-
palette**, **one type family**, generous **air** around everything, and
|
|
17
|
-
text that **fits at a readable size**. The viewer knows within a second
|
|
18
|
-
where to look first. Every element on the slide is *content* (a title, a
|
|
19
|
-
claim, a list, a picture, a table, a diagram, an equation) or *structure*
|
|
20
|
-
(a surface, a rule, a caption); there is nothing whose only job is to
|
|
21
|
-
decorate. When the content is strong the slide needs nothing else, and
|
|
22
|
-
when the content is thin, ornament will not save it.
|
|
23
|
-
|
|
24
|
-
## 1. Start from the content
|
|
25
|
-
|
|
26
|
-
Before drawing anything, write down for each slide:
|
|
27
|
-
|
|
28
|
-
- **Purpose** — one sentence: what the viewer should know or feel after it.
|
|
29
|
-
- **Carrier** — the single element that delivers the purpose: a
|
|
30
|
-
statement, a short list, a picture, a table, a process, an equation.
|
|
31
|
-
- **Support** — at most two more elements that help the carrier (a
|
|
32
|
-
caption, a source, a question, a picture beside a list).
|
|
33
|
-
- **Archetype** — pick one from the catalog in §10. Reusing a handful of
|
|
34
|
-
archetypes is what makes a deck feel consistent.
|
|
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
|
-
s
|
|
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
|
-
|
|
1
|
+
# Slide design: making a generated deck look designed
|
|
2
|
+
|
|
3
|
+
`space-aware-authoring.md` makes a deck *physically* correct — nothing
|
|
4
|
+
overflows, nothing falls off the slide. This file is about the other
|
|
5
|
+
half: a deck that looks like a designer laid it out. Everything here is
|
|
6
|
+
built from the public API (`BBox`, `add_text`, `add_card`, `add_bullets`,
|
|
7
|
+
`add_picture_fit`, `PALETTES`, `pptx2.diagrams`, `render_contact_sheet`),
|
|
8
|
+
so the code samples run as written.
|
|
9
|
+
|
|
10
|
+
Read this when the deck will be *seen* — a lesson, a talk, a pitch — and
|
|
11
|
+
not only parsed.
|
|
12
|
+
|
|
13
|
+
## The one-paragraph brief
|
|
14
|
+
|
|
15
|
+
A slide that looks designed has **one idea**, **one focal point**, **one
|
|
16
|
+
palette**, **one type family**, generous **air** around everything, and
|
|
17
|
+
text that **fits at a readable size**. The viewer knows within a second
|
|
18
|
+
where to look first. Every element on the slide is *content* (a title, a
|
|
19
|
+
claim, a list, a picture, a table, a diagram, an equation) or *structure*
|
|
20
|
+
(a surface, a rule, a caption); there is nothing whose only job is to
|
|
21
|
+
decorate. When the content is strong the slide needs nothing else, and
|
|
22
|
+
when the content is thin, ornament will not save it.
|
|
23
|
+
|
|
24
|
+
## 1. Start from the content
|
|
25
|
+
|
|
26
|
+
Before drawing anything, write down for each slide:
|
|
27
|
+
|
|
28
|
+
- **Purpose** — one sentence: what the viewer should know or feel after it.
|
|
29
|
+
- **Carrier** — the single element that delivers the purpose: a
|
|
30
|
+
statement, a short list, a picture, a table, a process, an equation.
|
|
31
|
+
- **Support** — at most two more elements that help the carrier (a
|
|
32
|
+
caption, a source, a question, a picture beside a list).
|
|
33
|
+
- **Archetype** — pick one from the catalog in §10. Reusing a handful of
|
|
34
|
+
archetypes is what makes a deck feel consistent.
|
|
35
|
+
|
|
36
|
+
Write the archetype sequence down before you write slide 2 (a comment at
|
|
37
|
+
the top of `main.py` is enough): a deck of 8–12 slides needs at least
|
|
38
|
+
five distinct archetypes, consecutive slides never share one, and no
|
|
39
|
+
archetype appears more than three times. Alternate dense and light
|
|
40
|
+
slides: after a table or a three-card slide, give the audience a
|
|
41
|
+
statement, a big number or a full-bleed picture. A deck whose content
|
|
42
|
+
slides are all "title + bullets" (or all "title + three cards") is not a
|
|
43
|
+
designed deck, however clean each slide is — rebuild it.
|
|
44
|
+
|
|
45
|
+
Plan the pictures at the same time. A hero photo on the title (or on the
|
|
46
|
+
most visual slide) plus a picture on roughly every third content slide
|
|
47
|
+
is the baseline for a classroom deck; fetch them before writing the
|
|
48
|
+
script so the layout is built around them rather than patched later.
|
|
49
|
+
|
|
50
|
+
### Who is in the room
|
|
51
|
+
|
|
52
|
+
Fix the audience before the first slide: nationality and language
|
|
53
|
+
(Czech, Slovak, Polish, …), school level and age, subject. Everything
|
|
54
|
+
downstream follows from it — the language and its native typography
|
|
55
|
+
(diacritics, „quotation marks“, decimal comma, local dates and
|
|
56
|
+
currency), the examples, names, places, institutions and curriculum
|
|
57
|
+
terms the students recognise (Czech: RVP/ŠVP, Kč, Praha, Karel IV.;
|
|
58
|
+
Slovak: ŠVP, Bratislava, Štúr; Polish: podstawa programowa, zł, Kraków,
|
|
59
|
+
Kopernik), the vocabulary pitched to the age group. Titles are claims
|
|
60
|
+
("Led je lehčí než voda"), not labels ("Hustota"); every bullet carries a
|
|
61
|
+
concrete fact, number, example or question. A slide that would fit any
|
|
62
|
+
school in any country is not finished.
|
|
63
|
+
|
|
64
|
+
## 2. Canvas, margins, grid
|
|
65
|
+
|
|
66
|
+
Work in 16:9 at 13.333 × 7.5 in, and set it explicitly:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from pptx2 import Presentation, BBox
|
|
70
|
+
from pptx2.util import Inches
|
|
71
|
+
|
|
72
|
+
prs = Presentation()
|
|
73
|
+
prs.slide_width, prs.slide_height = Inches(13.333), Inches(7.5)
|
|
74
|
+
W, H = 13.333, 7.5
|
|
75
|
+
BLANK = prs.slide_layouts[6]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then let two regions govern every content slide:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
TITLE = BBox.from_inches(0.8, 0.55, W - 1.6, 0.9) # title band
|
|
82
|
+
BODY = BBox.from_inches(0.8, 1.7, W - 1.6, H - 2.4) # content area (ends at 6.8")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Margins**: 0.8 in left/right, 0.55 in top, 0.7 in bottom. Nothing but
|
|
86
|
+
a full-bleed picture or a coloured background touches the slide edge.
|
|
87
|
+
- **Gutters**: 0.4–0.6 in between columns and cards. Use one value per deck.
|
|
88
|
+
- **Columns**: `BODY.columns(3, gap=Inches(0.5))`,
|
|
89
|
+
`BODY.split_h([7, 5], gap=Inches(0.6))`, `BODY.rows(2, gap=...)`. Every
|
|
90
|
+
card, column and picture on a slide starts at the same left edge as the
|
|
91
|
+
title, or at a grid line derived from it.
|
|
92
|
+
- **Optical center**: single statements and hero titles sit slightly
|
|
93
|
+
above the geometric middle (`anchor="middle"` in a box whose top is
|
|
94
|
+
~1.6 in and bottom ~5.4 in).
|
|
95
|
+
|
|
96
|
+
## 3. Typography
|
|
97
|
+
|
|
98
|
+
One family for the whole deck. Noto Sans (or whatever the environment's
|
|
99
|
+
default sans is) is a fine choice; the design comes from *scale* and
|
|
100
|
+
*spacing*, not from the font.
|
|
101
|
+
|
|
102
|
+
| Role | Size | Weight | Notes |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| Hero title (title / section slide) | 48–60 pt | bold | 1–2 lines |
|
|
105
|
+
| Statement (the one-sentence slide) | 36–44 pt | bold | ≤ 20 words, centred or left |
|
|
106
|
+
| Big number | 96–120 pt | bold, `accent` | one figure + a 24 pt explanation |
|
|
107
|
+
| Slide title | 32–36 pt (never below 28) | bold | one line; sentence case |
|
|
108
|
+
| Body / bullets | 22–26 pt (never below 20) | regular | ≤ 6 items, ≤ 12 words each |
|
|
109
|
+
| Card title | 22–24 pt | bold | |
|
|
110
|
+
| Card body | 18–20 pt (never below 18) | regular | 2–4 lines |
|
|
111
|
+
| Table cells | 18–20 pt | header bold | |
|
|
112
|
+
| Caption / source / footer | 14 pt | regular or italic, `muted` | |
|
|
113
|
+
|
|
114
|
+
Nothing on a slide is under 14 pt: a projected slide is read from the
|
|
115
|
+
back of a room, and a font that only fits by going smaller is a sign of
|
|
116
|
+
too much text, not of a small box. Cut words or split the slide; never
|
|
117
|
+
solve density with a smaller size.
|
|
118
|
+
|
|
119
|
+
Rules that make type look intentional:
|
|
120
|
+
|
|
121
|
+
- **Sentence case** for titles ("Co rostlina potřebuje", not "CO ROSTLINA
|
|
122
|
+
POTŘEBUJE"). Uppercase belongs to acronyms.
|
|
123
|
+
- **Left-align running text.** Centre only short statements, titles on
|
|
124
|
+
hero slides, and labels inside diagram nodes.
|
|
125
|
+
- **Breathing room**: `line_spacing = 1.1–1.2` for body, `space_after =
|
|
126
|
+
8–10 pt` between bullets (`add_bullets(gap_pt=8)` does this).
|
|
127
|
+
- **Zero text-box margins** when the box is already placed by the grid
|
|
128
|
+
(`margin_pt=0`) so the text edge *is* the grid line.
|
|
129
|
+
- **Hierarchy by size and weight, not by colour.** Reserve colour for the
|
|
130
|
+
one thing on the slide that should pop (§4).
|
|
131
|
+
- **Fit, then check**: `add_bullets` and `add_card` shrink text to the box
|
|
132
|
+
and never below 18 pt (bullets) / 16 pt (card body). If they had to
|
|
133
|
+
shrink, there is too much text — split the slide. `audit()` reports a
|
|
134
|
+
`MinFontSize` warning for any run under 12 pt.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
slide.shapes.add_text(TITLE, text="Co rostlina potřebuje",
|
|
138
|
+
size_pt=32, bold=True, color=P.ink,
|
|
139
|
+
anchor="middle", margin_pt=0)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 4. Colour
|
|
143
|
+
|
|
144
|
+
Pick **one** palette and use only its roles:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from pptx2 import PALETTES
|
|
148
|
+
|
|
149
|
+
P = PALETTES["slate"] # slate · linen · forest · plum · ember · navy · graphite
|
|
150
|
+
D = P.dark() # same hues on a dark paper — title, section, closing slides
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Role | Use |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `P.paper` | slide background (`slide.background.fill.solid(); ...fore_color.rgb = P.paper`) |
|
|
156
|
+
| `P.ink` | all headings and body text |
|
|
157
|
+
| `P.muted` | captions, sources, secondary labels, table grid text |
|
|
158
|
+
| `P.surface` | card fills, table zebra rows, picture frames |
|
|
159
|
+
| `P.line` | hairline rules and table borders |
|
|
160
|
+
| `P.accent` | **one** emphasis per slide: the key number, the active step, a highlighted word, the header row |
|
|
161
|
+
| `P.accent_soft` | fill of a callout card / highlighted row; text on it stays `P.ink` |
|
|
162
|
+
| `P.accent_ink` | text placed *on* `P.accent` (white on a dark accent) |
|
|
163
|
+
|
|
164
|
+
- **One accent per slide.** Two accents split attention; three look like a
|
|
165
|
+
toy. Cards in a row all share `P.surface`; the one to emphasise (if any)
|
|
166
|
+
gets `P.accent_soft`.
|
|
167
|
+
- **Dark slides** for the title, section dividers and the closing slide
|
|
168
|
+
give the deck rhythm. Use `D = P.dark()` so the hues stay in the family.
|
|
169
|
+
- **Contrast**: every text/background pair ≥ 4.5:1 (the linter reports
|
|
170
|
+
`LowContrast`); the shipped palettes satisfy this for `ink` on `paper`,
|
|
171
|
+
`surface` and `accent_soft`, for `muted` on `paper`, and for `accent_ink`
|
|
172
|
+
on `accent`.
|
|
173
|
+
- **Photos bring their own colour.** Beside a photo, keep the rest of the
|
|
174
|
+
slide to `paper` / `ink` / `muted`.
|
|
175
|
+
|
|
176
|
+
## 5. Surfaces: cards, callouts, panels
|
|
177
|
+
|
|
178
|
+
A card is one tinted rounded rectangle with padded text inside it, and
|
|
179
|
+
that is the whole card. Its silhouette stays a clean rectangle from every
|
|
180
|
+
angle; emphasis comes from the tint (or, when the tint is unwanted, a
|
|
181
|
+
hairline outline — one or the other, never both). Padding of 18–28 pt on
|
|
182
|
+
every side gives the text room to sit rather than press against the edge.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from pptx2 import add_card
|
|
186
|
+
|
|
187
|
+
for cell, (title, body) in zip(BODY.columns(3, gap=Inches(0.5)), items):
|
|
188
|
+
add_card(slide, cell, title=title, body=body,
|
|
189
|
+
fill=P.surface, title_color=P.ink, body_color=P.ink,
|
|
190
|
+
title_size_pt=22, body_size_pt=18, pad_pt=22)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Composition rules for groups of cards:
|
|
194
|
+
|
|
195
|
+
- **2–4 cards per row**, equal size, equal gap, same fill, same radius.
|
|
196
|
+
Five or more become a table.
|
|
197
|
+
- **Same content shape in every card**: if one has a title and two lines,
|
|
198
|
+
all have a title and two lines. Trim copy to match.
|
|
199
|
+
- **A callout** (question, key definition, "remember this") is one wide
|
|
200
|
+
card in `P.accent_soft`, inset from the body region so it reads as a
|
|
201
|
+
deliberate object, not as a background.
|
|
202
|
+
- **Cards hold content, backgrounds hold cards.** A card sits directly on
|
|
203
|
+
the paper; it does not sit on another panel.
|
|
204
|
+
- **Numbering** belongs *in* the title text ("1 · Zachycení"), where it
|
|
205
|
+
aligns with everything else, rather than in a separate badge shape.
|
|
206
|
+
|
|
207
|
+
`add_card` returns `card.inner` (the padded box) so a picture, equation
|
|
208
|
+
or diagram can be placed inside the same surface:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
c = add_card(slide, right_col, fill=P.surface)
|
|
212
|
+
slide.shapes.add_equation(c.inner, latex=r"6\,CO_2 + 6\,H_2O \rightarrow C_6H_{12}O_6 + 6\,O_2", size_pt=28)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Code listings
|
|
216
|
+
|
|
217
|
+
Source code is `add_code`, never a text box with a monospace font: it
|
|
218
|
+
writes one paragraph per line, verbatim, unwrapped, in a monospace face
|
|
219
|
+
on a rounded editor-coloured surface, and *names the shape*
|
|
220
|
+
`fika:code:<language>:<theme>:<0|1>`. An editor with a native code block
|
|
221
|
+
(Fika) turns that shape into a syntax-highlighted, editable code element
|
|
222
|
+
on import; PowerPoint shows a clean monospace box. A listing is a visual
|
|
223
|
+
— it takes a column or the body, 18–20 pt, at most ~12 lines; longer
|
|
224
|
+
code is split across slides, explained beside it, not shrunk.
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
left, right = BODY.split_h([5, 7], gap=Inches(0.6))
|
|
228
|
+
add_bullets(s, left, items=["if – když platí podmínka", "else – jinak"], size_pt=22, color=P.ink)
|
|
229
|
+
add_code(s, right, code='if (odpoved == "ano")\n{\n Console.WriteLine("Skvělé!");\n}',
|
|
230
|
+
language="csharp", theme="github-dark", size_pt=18)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## 6. Whitespace and alignment
|
|
234
|
+
|
|
235
|
+
- Air lives in the margins and gutters — not in a half-empty body. Content
|
|
236
|
+
fills `BODY`: a row of cards spans its full width *and* height
|
|
237
|
+
(`BODY.columns(n)`), two columns split it evenly, a picture takes its
|
|
238
|
+
whole column. Never hand-size a few 3 in boxes and float them in an
|
|
239
|
+
11.7 in area. When the content is short, use fewer, larger elements
|
|
240
|
+
(two big cards, one big number, a statement) rather than small ones
|
|
241
|
+
with dead space around them.
|
|
242
|
+
- **One left edge.** Title, bullets, cards and pictures share the `0.8 in`
|
|
243
|
+
margin (or a grid line derived from it). Right edges align to the
|
|
244
|
+
mirror margin.
|
|
245
|
+
- **Consistent vertical rhythm.** Title band → 0.25 in gap → body. Every
|
|
246
|
+
content slide places its body at the same `BODY.top`, so flipping
|
|
247
|
+
through the deck feels steady.
|
|
248
|
+
- **Balance, not symmetry.** A picture on the right with a list on the
|
|
249
|
+
left is balanced when the list's text block is roughly as tall as the
|
|
250
|
+
picture. Use `split_h([7, 5])` for text-heavy pairs, `[1, 1]` for a
|
|
251
|
+
true comparison.
|
|
252
|
+
- **Empty is fine, unbalanced is not.** A slide whose right half is blank
|
|
253
|
+
because the list is short: make the list larger (24 pt), add a picture,
|
|
254
|
+
or narrow the text column and centre the pair.
|
|
255
|
+
|
|
256
|
+
## 7. Pictures
|
|
257
|
+
|
|
258
|
+
Photos carry emotion and specificity — one good photo does more than three
|
|
259
|
+
small ones.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from pptx2 import add_picture_fit
|
|
263
|
+
|
|
264
|
+
# Beside text: fit inside the column, never distort, caption in muted.
|
|
265
|
+
add_picture_fit(slide, "leaf.jpg", right_col, mode="contain",
|
|
266
|
+
caption="Buk lesní · Wikimedia Commons", caption_color=P.muted)
|
|
267
|
+
|
|
268
|
+
# Full-bleed hero: crop to fill the whole slide, then a dark band for text.
|
|
269
|
+
with slide.design_group("hero"): # photo + band + title are one deliberate stack
|
|
270
|
+
add_picture_fit(slide, "forest.jpg", BBox.from_inches(0, 0, W, H), mode="cover")
|
|
271
|
+
band = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, *BBox.from_inches(0, H - 2.2, W, 2.2))
|
|
272
|
+
band.fill_hex(D.paper); band.line.fill.background(); band.shadow.clear()
|
|
273
|
+
band.fill.fore_color.alpha = 0.8 # translucent, photo shows through
|
|
274
|
+
slide.shapes.add_text(BBox.from_inches(0.8, H - 1.9, W - 1.6, 1.6),
|
|
275
|
+
text="Les jako plíce planety", size_pt=44, bold=True,
|
|
276
|
+
color=D.ink, anchor="middle", margin_pt=0)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
- **`mode="contain"`** when the whole picture matters (diagrams, maps,
|
|
280
|
+
artworks); **`mode="cover"`** for atmosphere (backgrounds, hero slides,
|
|
281
|
+
square tiles in a grid).
|
|
282
|
+
- **Picture grids**: `BODY.columns(3)` → three `mode="cover"` tiles of
|
|
283
|
+
equal size, one caption each.
|
|
284
|
+
- **Caption every photo** that is not purely decorative: what it shows and
|
|
285
|
+
the source, 12–14 pt, `P.muted`.
|
|
286
|
+
- Leave pictures unframed. A `P.surface` background behind a letter-boxed
|
|
287
|
+
`contain` picture is the one exception, so the empty band reads as a
|
|
288
|
+
frame rather than a gap.
|
|
289
|
+
|
|
290
|
+
## 8. Tables, charts, diagrams
|
|
291
|
+
|
|
292
|
+
**Tables** — structure carries the styling:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
tbl = slide.shapes.add_table(rows, cols, *BODY.inset(bottom=Inches(1.0)), style="clean").table
|
|
296
|
+
tbl.format_cells(rows=0, fill=P.ink, color=P.paper, bold=True, size_pt=18)
|
|
297
|
+
tbl.format_cells(rows=slice(1, None), size_pt=18, color=P.ink)
|
|
298
|
+
tbl.format_cells(rows=slice(1, None, 2), fill=P.surface) # zebra
|
|
299
|
+
tbl.format_cells(rows=slice(1, None), cols=0, bold=True) # row labels
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
≤ 6 body rows and ≤ 4 columns on a slide; more belongs in a handout. The
|
|
303
|
+
header row is the accent of the slide — use `P.ink` or `P.accent`, not
|
|
304
|
+
both.
|
|
305
|
+
|
|
306
|
+
**Diagrams** — `pptx2.diagrams` recipes already space nodes and route
|
|
307
|
+
arrows; feed them the palette:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from pptx2.diagrams import horizontal_pipeline, cycle, hub_and_spoke, comparison_columns
|
|
311
|
+
|
|
312
|
+
horizontal_pipeline(slide, BODY.inset(top=Inches(1.4), bottom=Inches(1.4)),
|
|
313
|
+
steps=["Vstupy", "Světlo + chlorofyl", "Výstupy"],
|
|
314
|
+
accent=P.accent, fill=P.surface, text_color=P.ink,
|
|
315
|
+
card_line=None, card_radius=10, size_pt=20)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
A pipeline with 3–5 steps, a cycle with 3–6 nodes, a hub with 3–6 spokes.
|
|
319
|
+
Node labels are 2–4 words; explanations go in a list or a card beside the
|
|
320
|
+
diagram.
|
|
321
|
+
|
|
322
|
+
**Charts** — `chart.recolour([P.accent, P.muted, P.line])`, gridlines in
|
|
323
|
+
`P.line`, one series in `P.accent` and the rest in greys when there is a
|
|
324
|
+
story to tell. Title the chart with the takeaway ("Kyslík roste s
|
|
325
|
+
osvětlením"), not the variable names.
|
|
326
|
+
|
|
327
|
+
**Equations** — `slide.shapes.add_equation(bb, latex=..., size_pt=28)` or
|
|
328
|
+
`paragraph.add_math(...)`; display equations at 28–36 pt, centred, with
|
|
329
|
+
air around them.
|
|
330
|
+
|
|
331
|
+
## 9. Overlaps: deliberate and accidental
|
|
332
|
+
|
|
333
|
+
Overlap is a normal design move — text on a card, a caption on a photo, a
|
|
334
|
+
translucent band over a hero image, a number disc on a timeline. The
|
|
335
|
+
linter's `ShapeCollision` warning asks one question: *did you mean it?*
|
|
336
|
+
|
|
337
|
+
- **Meant it** → say so, and the warning disappears:
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
with slide.design_group("hero"): # everything created inside is one cluster
|
|
341
|
+
add_picture_fit(slide, "forest.jpg", BBox.from_slide(slide), mode="cover")
|
|
342
|
+
band = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, *BBox.from_inches(0, H - 2.2, W, 2.2))
|
|
343
|
+
slide.shapes.add_text(BBox.from_inches(0.8, H - 1.9, W - 1.6, 1.6), text="…")
|
|
344
|
+
|
|
345
|
+
slide.lint_group_overlaps(card, picture, caption) # shapes already made, widest first
|
|
346
|
+
badge.allow_overlap_with(card) # exactly this pair
|
|
347
|
+
card.layer, badge.layer_above = "card", "card" # also asserts z-order
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
`add_card`, `add_picture_fit`, the components and the diagram recipes
|
|
351
|
+
already tag their own shapes, so a card and its text never trigger it.
|
|
352
|
+
|
|
353
|
+
- **Did not mean it** → it is a layout bug: two text boxes crossing, a
|
|
354
|
+
card poking into its neighbour, a picture over a title. Fix the
|
|
355
|
+
geometry (use `columns()` / `split_h()` instead of hand arithmetic) and
|
|
356
|
+
re-lint.
|
|
357
|
+
|
|
358
|
+
Deliberate stacking looks deliberate when the upper shape is clearly
|
|
359
|
+
*inside* the lower one (inset on every side) or clearly *anchored* to one
|
|
360
|
+
of its corners/edges. A shape that half-crosses a boundary reads as a
|
|
361
|
+
mistake even when it is intentional.
|
|
362
|
+
|
|
363
|
+
`slide.tidy()` runs the safe auto-fixes (`OffSlide`, `TextOverflow`,
|
|
364
|
+
`OffGridDrift`); collisions, contrast and minimum size stay as warnings
|
|
365
|
+
because they need a judgment call. Make it, then move on.
|
|
366
|
+
|
|
367
|
+
## 10. Archetype catalog
|
|
368
|
+
|
|
369
|
+
Each archetype below is complete: the code runs given the setup in §2 and
|
|
370
|
+
`P`/`D` from §4. Mix 4–5 of them across a deck.
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
def paper(slide, color):
|
|
374
|
+
slide.background.fill.solid()
|
|
375
|
+
slide.background.fill.fore_color.rgb = color
|
|
376
|
+
|
|
377
|
+
def content_slide(title):
|
|
378
|
+
"""Title band + body region. Every content archetype starts here."""
|
|
379
|
+
s = prs.slides.add_slide(BLANK)
|
|
380
|
+
paper(s, P.paper)
|
|
381
|
+
s.shapes.add_text(TITLE, text=title, size_pt=32, bold=True,
|
|
382
|
+
color=P.ink, anchor="middle", margin_pt=0)
|
|
383
|
+
return s
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Title (dark)** — hero title, one-line subtitle, nothing else.
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
s = prs.slides.add_slide(BLANK); paper(s, D.paper)
|
|
390
|
+
s.shapes.add_text(BBox.from_inches(0.9, 2.2, W - 1.8, 1.6), text="Fotosyntéza",
|
|
391
|
+
size_pt=54, bold=True, color=D.ink, anchor="bottom", margin_pt=0)
|
|
392
|
+
s.shapes.add_text(BBox.from_inches(0.9, 3.9, W - 1.8, 0.8),
|
|
393
|
+
text="Jak rostliny mění světlo v život · 7. ročník",
|
|
394
|
+
size_pt=22, color=D.muted, margin_pt=0)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Section divider (dark)** — number + section name, optically centred.
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
s = prs.slides.add_slide(BLANK); paper(s, D.paper)
|
|
401
|
+
s.shapes.add_text(BBox.from_inches(0.9, 2.4, W - 1.8, 0.7), text="Část 2",
|
|
402
|
+
size_pt=20, color=D.accent, margin_pt=0)
|
|
403
|
+
s.shapes.add_text(BBox.from_inches(0.9, 3.0, W - 1.8, 1.4), text="Co se děje v listu",
|
|
404
|
+
size_pt=44, bold=True, color=D.ink, margin_pt=0)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Statement** — one sentence the audience should remember; optional
|
|
408
|
+
follow-up question in the accent.
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
s = prs.slides.add_slide(BLANK); paper(s, P.paper)
|
|
412
|
+
s.shapes.add_text(BBox.from_inches(1.2, 1.8, W - 2.4, 3.2),
|
|
413
|
+
text="Každý nádech kyslíku kdysi vyrobila rostlina.",
|
|
414
|
+
size_pt=40, bold=True, color=P.ink, anchor="middle", align="center")
|
|
415
|
+
s.shapes.add_text(BBox.from_inches(1.2, 5.2, W - 2.4, 0.6),
|
|
416
|
+
text="Proč je fotosyntéza nejdůležitější reakce na Zemi?",
|
|
417
|
+
size_pt=18, color=P.accent, align="center")
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Bullets + picture** — the workhorse. List left (7/12), picture right
|
|
421
|
+
(5/12), caption under the picture.
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
s = content_slide("Co rostlina potřebuje")
|
|
425
|
+
left, right = BODY.split_h([7, 5], gap=Inches(0.6))
|
|
426
|
+
add_bullets(s, left, items=[
|
|
427
|
+
"Světlo — energii dodává Slunce",
|
|
428
|
+
"Vodu — kořeny ji přivádějí z půdy",
|
|
429
|
+
"Oxid uhličitý — vstupuje průduchy",
|
|
430
|
+
"Chlorofyl — zelené barvivo, které světlo zachytí",
|
|
431
|
+
], size_pt=22, color=P.ink)
|
|
432
|
+
add_picture_fit(s, "leaf.jpg", right, mode="contain",
|
|
433
|
+
caption="List buku · Wikimedia Commons", caption_color=P.muted)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Three cards** — parallel concepts, equal weight.
|
|
437
|
+
|
|
438
|
+
```python
|
|
439
|
+
s = content_slide("Tři fáze v jednom listu")
|
|
440
|
+
for cell, (t, b) in zip(BODY.columns(3, gap=Inches(0.5)), [
|
|
441
|
+
("1 · Zachycení", "Chlorofyl v chloroplastech pohltí světelnou energii."),
|
|
442
|
+
("2 · Rozklad vody", "Energie rozdělí vodu na kyslík a vodík."),
|
|
443
|
+
("3 · Tvorba cukru", "Vodík se spojí s CO₂ a vzniká glukóza."),
|
|
444
|
+
]):
|
|
445
|
+
add_card(s, cell, title=t, body=b, fill=P.surface,
|
|
446
|
+
title_color=P.ink, body_color=P.ink, title_size_pt=22, body_size_pt=18)
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**Process** — steps with arrows, vertically centred in the body.
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
s = content_slide("Rovnice fotosyntézy jako proces")
|
|
453
|
+
horizontal_pipeline(s, BODY.inset(top=Inches(1.4), bottom=Inches(1.4)),
|
|
454
|
+
steps=["6 CO₂ + 6 H₂O", "světlo + chlorofyl", "C₆H₁₂O₆ + 6 O₂"],
|
|
455
|
+
accent=P.accent, fill=P.surface, text_color=P.ink,
|
|
456
|
+
card_line=None, card_radius=10, size_pt=20)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Comparison table** — two things side by side, row labels bold.
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
s = content_slide("Fotosyntéza vs. dýchání")
|
|
463
|
+
rows = [("", "Fotosyntéza", "Dýchání"), ("Kde", "chloroplasty", "mitochondrie"),
|
|
464
|
+
("Kdy", "jen na světle", "stále"), ("Vstup", "CO₂ + H₂O", "glukóza + O₂"),
|
|
465
|
+
("Výstup", "glukóza + O₂", "CO₂ + H₂O + energie")]
|
|
466
|
+
tbl = s.shapes.add_table(len(rows), 3, *BODY.inset(bottom=Inches(1.0)), style="clean").table
|
|
467
|
+
for r, row in enumerate(rows):
|
|
468
|
+
for c, val in enumerate(row):
|
|
469
|
+
tbl.cell(r, c).text = val
|
|
470
|
+
tbl.format_cells(rows=0, fill=P.ink, color=P.paper, bold=True, size_pt=18)
|
|
471
|
+
tbl.format_cells(rows=slice(1, None), size_pt=18, color=P.ink)
|
|
472
|
+
tbl.format_cells(rows=slice(1, None), cols=0, bold=True)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Two-column contrast** — "before / after", "myth / fact", "pros / cons".
|
|
476
|
+
|
|
477
|
+
```python
|
|
478
|
+
s = content_slide("Mýtus a skutečnost")
|
|
479
|
+
a, b = BODY.columns(2, gap=Inches(0.6))
|
|
480
|
+
add_card(s, a, title="Mýtus", body="Rostliny dýchají jen v noci.",
|
|
481
|
+
fill=P.surface, title_color=P.muted, body_color=P.ink, title_size_pt=20, body_size_pt=20)
|
|
482
|
+
add_card(s, b, title="Skutečnost", body="Dýchají nepřetržitě; ve dne fotosyntéza převažuje.",
|
|
483
|
+
fill=P.accent_soft, title_color=P.ink, body_color=P.ink, title_size_pt=20, body_size_pt=20)
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Full-bleed picture with a title band** — see §7 (`mode="cover"` + a
|
|
487
|
+
translucent band in `D.paper` + title in `D.ink`).
|
|
488
|
+
|
|
489
|
+
**Quote** — large italic statement, attribution in `muted`.
|
|
490
|
+
|
|
491
|
+
```python
|
|
492
|
+
s = prs.slides.add_slide(BLANK); paper(s, P.paper)
|
|
493
|
+
s.shapes.add_text(BBox.from_inches(1.4, 1.8, W - 2.8, 3.0),
|
|
494
|
+
text="„Rostlina je stroj na slunce.“", size_pt=40, italic=True,
|
|
495
|
+
color=P.ink, anchor="middle", align="center")
|
|
496
|
+
s.shapes.add_text(BBox.from_inches(1.4, 4.9, W - 2.8, 0.6), text="— Jan Evangelista Purkyně",
|
|
497
|
+
size_pt=18, color=P.muted, align="center")
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
**Question / activity** — one callout card, numbered prompts.
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
s = content_slide("Zamyslete se")
|
|
504
|
+
add_card(s, BODY.inset(left=Inches(1.5), right=Inches(1.5), top=Inches(0.6), bottom=Inches(0.6)),
|
|
505
|
+
title="Otázka do dvojic", body=[
|
|
506
|
+
"Proč jsou listy zelené, když černá pohltí nejvíce světla?",
|
|
507
|
+
"Co by se stalo s kyslíkem, kdyby zmizely oceánské řasy?",
|
|
508
|
+
], numbered=True, fill=P.accent_soft, title_color=P.ink, body_color=P.ink,
|
|
509
|
+
title_size_pt=24, body_size_pt=20, pad_pt=28)
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Summary / closing (dark)** — three takeaways at 24 pt, then stop.
|
|
513
|
+
|
|
514
|
+
```python
|
|
515
|
+
s = prs.slides.add_slide(BLANK); paper(s, D.paper)
|
|
516
|
+
s.shapes.add_text(BBox.from_inches(0.9, 1.4, W - 1.8, 1.0), text="Shrnutí",
|
|
517
|
+
size_pt=36, bold=True, color=D.ink, margin_pt=0)
|
|
518
|
+
add_bullets(s, BBox.from_inches(0.9, 2.6, W - 1.8, 3.6), items=[
|
|
519
|
+
"Rostliny přeměňují světlo, vodu a CO₂ na cukr a kyslík.",
|
|
520
|
+
"Děje se to v chloroplastech díky chlorofylu.",
|
|
521
|
+
"Bez fotosyntézy by nebyl kyslík ani potrava.",
|
|
522
|
+
], size_pt=24, color=D.ink)
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
## 11. Look at it before you ship it
|
|
526
|
+
|
|
527
|
+
Text previews cannot show a clipped title or a lopsided slide. Render the
|
|
528
|
+
deck once and look:
|
|
529
|
+
|
|
530
|
+
```python
|
|
531
|
+
for s in prs.slides:
|
|
532
|
+
s.tidy()
|
|
533
|
+
prs.save("output/deck.pptx")
|
|
534
|
+
prs.render_contact_sheet("preview.png", cols=4, thumb_width=480) # one PNG, every slide
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
What to check on the sheet, in order:
|
|
538
|
+
|
|
539
|
+
1. **Fit** — every title on one line, no text touching a box edge, no
|
|
540
|
+
list shrunk to small type.
|
|
541
|
+
2. **Rhythm** — titles at the same height on every content slide; the same
|
|
542
|
+
left edge everywhere; dark slides where you intended them.
|
|
543
|
+
3. **Density** — no slide markedly fuller than its neighbours; no half-empty
|
|
544
|
+
slide next to a packed one.
|
|
545
|
+
4. **Colour** — one accent per slide; nothing outside the palette except
|
|
546
|
+
photos.
|
|
547
|
+
5. **Pictures** — undistorted, cropped where intended, captioned.
|
|
548
|
+
|
|
549
|
+
Fix what you see, re-render once, save. Two passes are enough; polishing
|
|
550
|
+
beyond that is time better spent on the words.
|