h17-sspdf 0.4.1 → 0.5.0
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.
- package/DOCUMENTATION.md +19 -3
- package/README.md +2 -1
- package/core/pdf-core.js +8 -4
- package/core/plugin-chart.js +19 -3
- package/core/render-document.js +19 -10
- package/examples/sources/og-sspdf.png +0 -0
- package/examples/sources/source-presentation.json +634 -0
- package/examples/themes/theme-presentation.js +276 -0
- package/package.json +1 -1
package/DOCUMENTATION.md
CHANGED
|
@@ -30,9 +30,11 @@ Controls page geometry, background, and baseline rendering state.
|
|
|
30
30
|
```js
|
|
31
31
|
page: {
|
|
32
32
|
// Page geometry
|
|
33
|
-
format: "a4", //
|
|
33
|
+
format: "a4", // named format: "a4", "letter", etc.
|
|
34
34
|
orientation: "portrait", // "portrait" or "landscape"
|
|
35
35
|
unit: "mm", // only "mm" is supported
|
|
36
|
+
pageWidthMm: 338, // custom width in mm (overrides format)
|
|
37
|
+
pageHeightMm: 190, // custom height in mm (overrides format)
|
|
36
38
|
compress: true, // PDF compression (default true)
|
|
37
39
|
|
|
38
40
|
// Margins - individual margins override the shared `margin` value
|
|
@@ -76,7 +78,9 @@ page: {
|
|
|
76
78
|
}
|
|
77
79
|
```
|
|
78
80
|
|
|
79
|
-
The content area runs from `marginTop` to `pageHeight - marginBottom` vertically, and `marginLeft` to `pageWidth - marginRight` horizontally. On A4 portrait, the page is 210
|
|
81
|
+
The content area runs from `marginTop` to `pageHeight - marginBottom` vertically, and `marginLeft` to `pageWidth - marginRight` horizontally. On A4 portrait, the page is 210 x 297 mm.
|
|
82
|
+
|
|
83
|
+
When `pageWidthMm` and `pageHeightMm` are both set, they override the `format` field and create a page with those exact dimensions. This is useful for non-standard formats like 16:9 presentations (e.g. 338 x 190 mm). All layout math -- margins, content area, pagination -- adapts automatically.
|
|
80
84
|
|
|
81
85
|
### `labels` (required)
|
|
82
86
|
|
|
@@ -235,6 +239,7 @@ Shared layout defaults read by operation handlers.
|
|
|
235
239
|
```js
|
|
236
240
|
layout: {
|
|
237
241
|
bulletIndentMm: 4.5, // text indent after bullet marker (default: 4)
|
|
242
|
+
chartAlign: "center", // chart horizontal alignment: "left" (default) or "center"
|
|
238
243
|
}
|
|
239
244
|
```
|
|
240
245
|
|
|
@@ -553,6 +558,16 @@ Three forms (use exactly one):
|
|
|
553
558
|
| `px` | number | Fixed space in CSS px |
|
|
554
559
|
| `label` | string | Theme label with `spaceMm` or `spacePx` |
|
|
555
560
|
|
|
561
|
+
#### `pageBreak`
|
|
562
|
+
|
|
563
|
+
Forces a new page. The cursor resets to the top content area. Page templates (headers/footers) are applied to the new page automatically.
|
|
564
|
+
|
|
565
|
+
```json
|
|
566
|
+
{ "type": "pageBreak" }
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
No fields required. Useful for presentation-style layouts where each section should start on its own page.
|
|
570
|
+
|
|
556
571
|
#### `hiddenText`
|
|
557
572
|
|
|
558
573
|
Renders text in the background color so it's invisible but present in the PDF text layer (for ATS keyword injection).
|
|
@@ -1714,11 +1729,12 @@ main();
|
|
|
1714
1729
|
| `chartType` | yes | string | Chart.js type: `"bar"`, `"line"`, `"doughnut"`, etc. |
|
|
1715
1730
|
| `data` | yes | object | Chart.js `data` config (`labels` + `datasets`) |
|
|
1716
1731
|
| `widthMm` | no | number | Width in the PDF in mm (default: content area width) |
|
|
1717
|
-
| `heightMm` | no | number | Height in
|
|
1732
|
+
| `heightMm` | no | number or `"fill"` | Height in mm (default: 80). `"fill"` uses remaining page space. |
|
|
1718
1733
|
| `canvasWidth` | no | number | Canvas render width in pixels (default: 1600) |
|
|
1719
1734
|
| `canvasHeight` | no | number | Canvas render height in pixels (default: 800) |
|
|
1720
1735
|
| `options` | no | object | Chart.js `options` object. `responsive: false` and `animation: false` are injected automatically. |
|
|
1721
1736
|
| `xMm` | no | number | Left edge in mm (default: content left margin) |
|
|
1737
|
+
| `align` | no | string | `"left"` (default) or `"center"`. Can also be set globally via `layout.chartAlign` in the theme. |
|
|
1722
1738
|
|
|
1723
1739
|
### Resolution
|
|
1724
1740
|
|
package/README.md
CHANGED
|
@@ -263,6 +263,7 @@ labels: {
|
|
|
263
263
|
| `divider` | Horizontal rule | `label`, `x1Mm`, `x2Mm` |
|
|
264
264
|
| `image` | Embedded PNG/JPEG | `src`, `width` (percentage or mm), `caption` |
|
|
265
265
|
| `spacer` | Vertical gap | `mm`, `px`, or `label` |
|
|
266
|
+
| `pageBreak` | Force new page | (none) |
|
|
266
267
|
| `hiddenText` | Invisible text | `label`, `text` |
|
|
267
268
|
| `quote` | Blockquote with attribution | `label`, `text`, `attribution` |
|
|
268
269
|
| `block` | Group children, optional background + border | `children`, `keepTogether` |
|
|
@@ -466,7 +467,7 @@ Claude Code skills for generating PDFs and themes are available in the `skills/`
|
|
|
466
467
|
|
|
467
468
|
## Constraints
|
|
468
469
|
|
|
469
|
-
- A4
|
|
470
|
+
- Page format defaults to A4; custom dimensions supported via `pageWidthMm`/`pageHeightMm` (e.g. 16:9 presentations)
|
|
470
471
|
- Single-line `row` cells, no multi-line column pairs
|
|
471
472
|
- `{{page}}` gives the current page number; `{{pages}}` (total page count) is not supported because keep-together rules make the final page count unpredictable until the last operation is laid out
|
|
472
473
|
- Charts require the `canvas` npm package (native C++ addon) for server-side rendering; everything else is zero native dependencies
|
package/core/pdf-core.js
CHANGED
|
@@ -77,6 +77,8 @@ class PDFCore {
|
|
|
77
77
|
* @param {number} [theme.page.margin]
|
|
78
78
|
* @param {string} [theme.page.format]
|
|
79
79
|
* @param {string} [theme.page.orientation]
|
|
80
|
+
* @param {number} [theme.page.pageWidthMm]
|
|
81
|
+
* @param {number} [theme.page.pageHeightMm]
|
|
80
82
|
* @param {string} [theme.page.unit]
|
|
81
83
|
* @param {boolean} [theme.page.compress]
|
|
82
84
|
* @param {number[]} [theme.page.backgroundColor]
|
|
@@ -102,11 +104,13 @@ class PDFCore {
|
|
|
102
104
|
this.margin = this.marginLeftMm;
|
|
103
105
|
this.headerHeightMm = Number(this.page.headerHeightMm) || 0;
|
|
104
106
|
this.footerHeightMm = Number(this.page.footerHeightMm) || 0;
|
|
105
|
-
|
|
107
|
+
const customW = Number(this.page.pageWidthMm) || 0;
|
|
108
|
+
const customH = Number(this.page.pageHeightMm) || 0;
|
|
109
|
+
const hasCustomDimensions = customW > 0 && customH > 0;
|
|
106
110
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
this.format = hasCustomDimensions
|
|
112
|
+
? [customW, customH]
|
|
113
|
+
: String(this.page.format || "a4").toLowerCase();
|
|
110
114
|
|
|
111
115
|
this.doc = new jsPDF({
|
|
112
116
|
orientation: this.page.orientation || "portrait",
|
package/core/plugin-chart.js
CHANGED
|
@@ -132,14 +132,30 @@ module.exports = {
|
|
|
132
132
|
);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
const
|
|
135
|
+
const { theme } = ctx;
|
|
136
|
+
const contentWidth = bounds.right - bounds.left;
|
|
137
|
+
const widthMm = operation.widthMm || contentWidth;
|
|
138
|
+
const heightMm = operation.heightMm === "fill"
|
|
139
|
+
? Math.max(0, core.contentBottomY - core.getCursorY())
|
|
140
|
+
: (operation.heightMm || 80);
|
|
141
|
+
const align = operation.align || (theme && theme.layout && theme.layout.chartAlign) || "left";
|
|
142
|
+
|
|
143
|
+
let x;
|
|
144
|
+
if (operation.xMm !== undefined) {
|
|
145
|
+
x = operation.xMm;
|
|
146
|
+
} else if (align === "center") {
|
|
147
|
+
x = bounds.left + (contentWidth - widthMm) / 2;
|
|
148
|
+
} else {
|
|
149
|
+
x = bounds.left;
|
|
150
|
+
}
|
|
138
151
|
|
|
139
152
|
core.drawImage({ data: operation._buf, format: 'PNG', x, widthMm, heightMm });
|
|
140
153
|
},
|
|
141
154
|
|
|
142
155
|
estimateHeight(ctx) {
|
|
156
|
+
if (ctx.operation.heightMm === "fill") {
|
|
157
|
+
return ctx.core.contentBottomY - ctx.core.getCursorY();
|
|
158
|
+
}
|
|
143
159
|
return (ctx.operation.heightMm || 80) + 4;
|
|
144
160
|
},
|
|
145
161
|
|
package/core/render-document.js
CHANGED
|
@@ -221,17 +221,21 @@ function executeOperations(ctx) {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
224
|
+
if (operation.type === "pageBreak") {
|
|
225
|
+
core.addPage();
|
|
226
|
+
} else {
|
|
227
|
+
core.withDocumentState(() => {
|
|
228
|
+
executeOperation({
|
|
229
|
+
core,
|
|
230
|
+
theme,
|
|
231
|
+
operation,
|
|
232
|
+
index,
|
|
233
|
+
templateMode,
|
|
234
|
+
templateBypassMargins,
|
|
235
|
+
insideContainer,
|
|
236
|
+
});
|
|
233
237
|
});
|
|
234
|
-
}
|
|
238
|
+
}
|
|
235
239
|
}
|
|
236
240
|
}
|
|
237
241
|
|
|
@@ -335,6 +339,7 @@ function isOperationType(type) {
|
|
|
335
339
|
|| type === "bullet"
|
|
336
340
|
|| type === "divider"
|
|
337
341
|
|| type === "spacer"
|
|
342
|
+
|| type === "pageBreak"
|
|
338
343
|
|| type === "hiddenText"
|
|
339
344
|
|| type === "table"
|
|
340
345
|
|| type === "image"
|
|
@@ -1026,6 +1031,10 @@ function estimateOperationHeight(ctx) {
|
|
|
1026
1031
|
throw new Error(`Spacer operation at index ${index} must provide mm, px, or label with spaceMm/spacePx`);
|
|
1027
1032
|
}
|
|
1028
1033
|
|
|
1034
|
+
if (operation.type === "pageBreak") {
|
|
1035
|
+
return 0;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1029
1038
|
if (operation.type === "hiddenText") {
|
|
1030
1039
|
return 0;
|
|
1031
1040
|
}
|
|
Binary file
|
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pageTemplates": {
|
|
3
|
+
"footer": [
|
|
4
|
+
{
|
|
5
|
+
"type": "divider",
|
|
6
|
+
"label": "slide.footer.rule",
|
|
7
|
+
"x1Mm": 24,
|
|
8
|
+
"x2Mm": 314
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"type": "row",
|
|
12
|
+
"leftLabel": "slide.footer.left",
|
|
13
|
+
"rightLabel": "slide.footer.right",
|
|
14
|
+
"leftText": "h17.ai | sspdf",
|
|
15
|
+
"rightText": "{{page}}",
|
|
16
|
+
"xLeftMm": 24,
|
|
17
|
+
"xRightMm": 314
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"footerHeightMm": 8,
|
|
21
|
+
"footerStartMm": 178,
|
|
22
|
+
"footerBypassMargins": false
|
|
23
|
+
},
|
|
24
|
+
"sections": [
|
|
25
|
+
{
|
|
26
|
+
"type": "section",
|
|
27
|
+
"comment": "Title slide",
|
|
28
|
+
"content": [
|
|
29
|
+
{
|
|
30
|
+
"type": "image",
|
|
31
|
+
"src": "examples/sources/og-sspdf.png",
|
|
32
|
+
"width": "70%",
|
|
33
|
+
"label": "slide.image"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "spacer",
|
|
37
|
+
"mm": 4
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "text",
|
|
41
|
+
"label": "slide.subtitle",
|
|
42
|
+
"text": "A math-first PDF engine. Send JSON, get a pixel-perfect document in under a second."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "divider",
|
|
46
|
+
"label": "slide.divider.accent",
|
|
47
|
+
"widthMm": 60
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "text",
|
|
51
|
+
"label": "slide.author",
|
|
52
|
+
"text": "Hugo Palma | h17.ai | March 2026"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"type": "pageBreak"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"type": "section",
|
|
61
|
+
"comment": "The problem",
|
|
62
|
+
"content": [
|
|
63
|
+
{
|
|
64
|
+
"type": "text",
|
|
65
|
+
"label": "slide.heading",
|
|
66
|
+
"text": "The problem"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"type": "divider",
|
|
70
|
+
"label": "slide.divider.accent",
|
|
71
|
+
"widthMm": 40
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "text",
|
|
75
|
+
"label": "slide.body",
|
|
76
|
+
"text": "Most PDF libraries force you to think in coordinates. You place text at (x, y), draw lines at absolute positions, and manually track where the cursor is. Change a font size and your entire layout breaks."
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"type": "spacer",
|
|
80
|
+
"mm": 4
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"type": "bullet",
|
|
84
|
+
"markerLabel": "slide.bullet.marker",
|
|
85
|
+
"label": "slide.bullet.text",
|
|
86
|
+
"text": "Layouts break when content length changes"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"type": "bullet",
|
|
90
|
+
"markerLabel": "slide.bullet.marker",
|
|
91
|
+
"label": "slide.bullet.text",
|
|
92
|
+
"text": "No automatic pagination or page-break logic"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"type": "bullet",
|
|
96
|
+
"markerLabel": "slide.bullet.marker",
|
|
97
|
+
"label": "slide.bullet.text",
|
|
98
|
+
"text": "Style changes require touching every draw call"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"type": "bullet",
|
|
102
|
+
"markerLabel": "slide.bullet.marker",
|
|
103
|
+
"label": "slide.bullet.text",
|
|
104
|
+
"text": "Themes, reuse, and consistency are an afterthought"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"type": "pageBreak"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"type": "section",
|
|
113
|
+
"comment": "Architecture",
|
|
114
|
+
"content": [
|
|
115
|
+
{
|
|
116
|
+
"type": "text",
|
|
117
|
+
"label": "slide.heading",
|
|
118
|
+
"text": "How sspdf works"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"type": "divider",
|
|
122
|
+
"label": "slide.divider.accent",
|
|
123
|
+
"widthMm": 40
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"type": "text",
|
|
127
|
+
"label": "slide.body",
|
|
128
|
+
"text": "Two inputs, one output. The theme defines every visual rule. The source defines content as a flat list of operations. The engine handles layout, pagination, and rendering."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"type": "spacer",
|
|
132
|
+
"mm": 4
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"type": "table",
|
|
136
|
+
"label": "slide.table",
|
|
137
|
+
"headerLabel": "slide.table.header",
|
|
138
|
+
"columns": [
|
|
139
|
+
{
|
|
140
|
+
"header": "Layer",
|
|
141
|
+
"widthMm": 80
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"header": "Responsibility",
|
|
145
|
+
"widthMm": 210
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"rows": [
|
|
149
|
+
[
|
|
150
|
+
"Theme (.js)",
|
|
151
|
+
"Page geometry, fonts, colors, margins, label styles"
|
|
152
|
+
],
|
|
153
|
+
[
|
|
154
|
+
"Source (.json)",
|
|
155
|
+
"Content: text, bullets, tables, charts, images, spacers"
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
"PDFCore",
|
|
159
|
+
"Cursor math, page breaks, style reset, drawing primitives"
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
"render-document",
|
|
163
|
+
"Reads source ops, resolves labels from theme, calls PDFCore"
|
|
164
|
+
],
|
|
165
|
+
[
|
|
166
|
+
"Plugins",
|
|
167
|
+
"Chart rendering (Chart.js), image embedding (PNG/JPEG)"
|
|
168
|
+
]
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"type": "pageBreak"
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"type": "section",
|
|
178
|
+
"comment": "Operation types",
|
|
179
|
+
"content": [
|
|
180
|
+
{
|
|
181
|
+
"type": "text",
|
|
182
|
+
"label": "slide.heading",
|
|
183
|
+
"text": "Operation types"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"type": "divider",
|
|
187
|
+
"label": "slide.divider.accent",
|
|
188
|
+
"widthMm": 40
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"type": "text",
|
|
192
|
+
"label": "slide.body",
|
|
193
|
+
"text": "Every document is built from these primitives. Each one is self-contained and flows automatically."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"type": "spacer",
|
|
197
|
+
"mm": 4
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"type": "table",
|
|
201
|
+
"label": "slide.table",
|
|
202
|
+
"headerLabel": "slide.table.header",
|
|
203
|
+
"columns": [
|
|
204
|
+
{
|
|
205
|
+
"header": "Operation",
|
|
206
|
+
"widthMm": 65
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"header": "What it does",
|
|
210
|
+
"widthMm": 225
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
"rows": [
|
|
214
|
+
[
|
|
215
|
+
"text",
|
|
216
|
+
"Single or multi-line text block with word wrapping and alignment"
|
|
217
|
+
],
|
|
218
|
+
[
|
|
219
|
+
"row",
|
|
220
|
+
"Left/right pair on the same line (dates, labels, key-value)"
|
|
221
|
+
],
|
|
222
|
+
[
|
|
223
|
+
"bullet",
|
|
224
|
+
"Bulleted item with text or vector shape markers"
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
"divider",
|
|
228
|
+
"Horizontal rule with configurable color, width, dash pattern"
|
|
229
|
+
],
|
|
230
|
+
[
|
|
231
|
+
"table",
|
|
232
|
+
"Multi-column table with headers, alt-row shading, page-break header repeat"
|
|
233
|
+
],
|
|
234
|
+
[
|
|
235
|
+
"chart",
|
|
236
|
+
"Bar, line, doughnut, pie via Chart.js (server-side canvas)"
|
|
237
|
+
],
|
|
238
|
+
[
|
|
239
|
+
"image",
|
|
240
|
+
"Embedded PNG/JPEG with percentage or explicit sizing"
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"spacer",
|
|
244
|
+
"Vertical gap in millimeters"
|
|
245
|
+
],
|
|
246
|
+
[
|
|
247
|
+
"section",
|
|
248
|
+
"Grouping wrapper, transparent to layout"
|
|
249
|
+
],
|
|
250
|
+
[
|
|
251
|
+
"hiddenText",
|
|
252
|
+
"Invisible metadata for PDF search indexing"
|
|
253
|
+
]
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"type": "pageBreak"
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"type": "section",
|
|
263
|
+
"comment": "Benchmark chart",
|
|
264
|
+
"content": [
|
|
265
|
+
{
|
|
266
|
+
"type": "text",
|
|
267
|
+
"label": "slide.heading",
|
|
268
|
+
"text": "Render performance"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"type": "divider",
|
|
272
|
+
"label": "slide.divider.accent",
|
|
273
|
+
"widthMm": 40
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"type": "text",
|
|
277
|
+
"label": "slide.body",
|
|
278
|
+
"text": "All documents render in under a second. Complex multi-page layouts with tables and charts stay well below 500ms."
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"type": "spacer",
|
|
282
|
+
"mm": 4
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"type": "chart",
|
|
286
|
+
"chartType": "bar",
|
|
287
|
+
"widthMm": 260,
|
|
288
|
+
"heightMm": "fill",
|
|
289
|
+
"canvasWidth": 2600,
|
|
290
|
+
"canvasHeight": 800,
|
|
291
|
+
|
|
292
|
+
"data": {
|
|
293
|
+
"labels": [
|
|
294
|
+
"Invoice",
|
|
295
|
+
"Article",
|
|
296
|
+
"Financial Report",
|
|
297
|
+
"Board Brief",
|
|
298
|
+
"Event Program",
|
|
299
|
+
"Certificate"
|
|
300
|
+
],
|
|
301
|
+
"datasets": [
|
|
302
|
+
{
|
|
303
|
+
"label": "Render time (ms)",
|
|
304
|
+
"data": [
|
|
305
|
+
42,
|
|
306
|
+
68,
|
|
307
|
+
187,
|
|
308
|
+
95,
|
|
309
|
+
112,
|
|
310
|
+
31
|
|
311
|
+
],
|
|
312
|
+
"backgroundColor": "rgba(0, 229, 255, 0.7)",
|
|
313
|
+
"borderColor": "rgba(0, 229, 255, 1)",
|
|
314
|
+
"borderWidth": 1
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
},
|
|
318
|
+
"options": {
|
|
319
|
+
"indexAxis": "y",
|
|
320
|
+
"scales": {
|
|
321
|
+
"x": {
|
|
322
|
+
"beginAtZero": true,
|
|
323
|
+
"ticks": {
|
|
324
|
+
"color": "rgba(122, 122, 136, 1)",
|
|
325
|
+
"font": {
|
|
326
|
+
"size": 18
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
"grid": {
|
|
330
|
+
"color": "rgba(30, 30, 40, 1)"
|
|
331
|
+
},
|
|
332
|
+
"title": {
|
|
333
|
+
"display": true,
|
|
334
|
+
"text": "Milliseconds",
|
|
335
|
+
"color": "rgba(122, 122, 136, 1)",
|
|
336
|
+
"font": {
|
|
337
|
+
"size": 18
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"y": {
|
|
342
|
+
"ticks": {
|
|
343
|
+
"color": "rgba(200, 200, 210, 1)",
|
|
344
|
+
"font": {
|
|
345
|
+
"size": 18
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
"grid": {
|
|
349
|
+
"display": false
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
"plugins": {
|
|
354
|
+
"legend": {
|
|
355
|
+
"display": false
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
"layout": {
|
|
359
|
+
"padding": {
|
|
360
|
+
"bottom": 5
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"type": "pageBreak"
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"type": "section",
|
|
372
|
+
"comment": "Theme system",
|
|
373
|
+
"content": [
|
|
374
|
+
{
|
|
375
|
+
"type": "text",
|
|
376
|
+
"label": "slide.heading",
|
|
377
|
+
"text": "Theme system"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"type": "divider",
|
|
381
|
+
"label": "slide.divider.accent",
|
|
382
|
+
"widthMm": 40
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"type": "text",
|
|
386
|
+
"label": "slide.body",
|
|
387
|
+
"text": "Themes are plain JS objects. Every visual decision lives in labels. Swap the theme file and the same source JSON renders in a completely different style."
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"type": "spacer",
|
|
391
|
+
"mm": 4
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"type": "row",
|
|
395
|
+
"leftLabel": "slide.row.left",
|
|
396
|
+
"rightLabel": "slide.row.right",
|
|
397
|
+
"leftText": "Built-in fonts",
|
|
398
|
+
"rightText": "20 Google Fonts shipped as base64 TTF (Inter, Roboto, Lora, Fira Code...)",
|
|
399
|
+
"xLeftMm": 24,
|
|
400
|
+
"xRightMm": 314
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"type": "row",
|
|
404
|
+
"leftLabel": "slide.row.left",
|
|
405
|
+
"rightLabel": "slide.row.right",
|
|
406
|
+
"leftText": "Vector bullets",
|
|
407
|
+
"rightText": "20 shape markers (arrows, checkmarks, diamonds) -- no Unicode issues",
|
|
408
|
+
"xLeftMm": 24,
|
|
409
|
+
"xRightMm": 314
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"type": "row",
|
|
413
|
+
"leftLabel": "slide.row.left",
|
|
414
|
+
"rightLabel": "slide.row.right",
|
|
415
|
+
"leftText": "Label properties",
|
|
416
|
+
"rightText": "Font, color, margin, padding, background, border, border-radius, left accent",
|
|
417
|
+
"xLeftMm": 24,
|
|
418
|
+
"xRightMm": 314
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
"type": "row",
|
|
422
|
+
"leftLabel": "slide.row.left",
|
|
423
|
+
"rightLabel": "slide.row.right",
|
|
424
|
+
"leftText": "Page templates",
|
|
425
|
+
"rightText": "Repeating headers and footers with page number tokens",
|
|
426
|
+
"xLeftMm": 24,
|
|
427
|
+
"xRightMm": 314
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
"type": "spacer",
|
|
431
|
+
"mm": 6
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"type": "text",
|
|
435
|
+
"label": "slide.callout",
|
|
436
|
+
"text": "This presentation is itself rendered by sspdf using a 338x190mm (16:9) custom page format with the h17.ai color palette."
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"type": "pageBreak"
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
"type": "section",
|
|
445
|
+
"comment": "Adoption chart",
|
|
446
|
+
"content": [
|
|
447
|
+
{
|
|
448
|
+
"type": "text",
|
|
449
|
+
"label": "slide.heading",
|
|
450
|
+
"text": "Test coverage and growth"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"type": "divider",
|
|
454
|
+
"label": "slide.divider.accent",
|
|
455
|
+
"widthMm": 40
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"type": "spacer",
|
|
459
|
+
"mm": 2
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
"type": "chart",
|
|
463
|
+
"chartType": "line",
|
|
464
|
+
"widthMm": 260,
|
|
465
|
+
"heightMm": "fill",
|
|
466
|
+
"canvasWidth": 2600,
|
|
467
|
+
"canvasHeight": 800,
|
|
468
|
+
|
|
469
|
+
"data": {
|
|
470
|
+
"labels": [
|
|
471
|
+
"v0.1.0",
|
|
472
|
+
"v0.1.2",
|
|
473
|
+
"v0.1.4",
|
|
474
|
+
"v0.1.6",
|
|
475
|
+
"v0.1.8",
|
|
476
|
+
"v0.2.0",
|
|
477
|
+
"v0.3.0",
|
|
478
|
+
"v0.4.0"
|
|
479
|
+
],
|
|
480
|
+
"datasets": [
|
|
481
|
+
{
|
|
482
|
+
"label": "Test count",
|
|
483
|
+
"data": [
|
|
484
|
+
8,
|
|
485
|
+
14,
|
|
486
|
+
22,
|
|
487
|
+
30,
|
|
488
|
+
38,
|
|
489
|
+
40,
|
|
490
|
+
42,
|
|
491
|
+
48
|
|
492
|
+
],
|
|
493
|
+
"borderColor": "rgba(0, 229, 255, 1)",
|
|
494
|
+
"backgroundColor": "rgba(0, 229, 255, 0.1)",
|
|
495
|
+
"fill": true,
|
|
496
|
+
"tension": 0.3,
|
|
497
|
+
"pointRadius": 6,
|
|
498
|
+
"pointBackgroundColor": "rgba(0, 229, 255, 1)",
|
|
499
|
+
"borderWidth": 3
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
"label": "Operation types",
|
|
503
|
+
"data": [
|
|
504
|
+
3,
|
|
505
|
+
4,
|
|
506
|
+
5,
|
|
507
|
+
7,
|
|
508
|
+
8,
|
|
509
|
+
9,
|
|
510
|
+
9,
|
|
511
|
+
10
|
|
512
|
+
],
|
|
513
|
+
"borderColor": "rgba(0, 165, 184, 1)",
|
|
514
|
+
"backgroundColor": "rgba(0, 165, 184, 0.05)",
|
|
515
|
+
"fill": true,
|
|
516
|
+
"tension": 0.3,
|
|
517
|
+
"pointRadius": 6,
|
|
518
|
+
"pointBackgroundColor": "rgba(0, 165, 184, 1)",
|
|
519
|
+
"borderWidth": 3
|
|
520
|
+
}
|
|
521
|
+
]
|
|
522
|
+
},
|
|
523
|
+
"options": {
|
|
524
|
+
"scales": {
|
|
525
|
+
"x": {
|
|
526
|
+
"ticks": {
|
|
527
|
+
"color": "rgba(122, 122, 136, 1)",
|
|
528
|
+
"font": {
|
|
529
|
+
"size": 18
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"grid": {
|
|
533
|
+
"color": "rgba(30, 30, 40, 1)"
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
"y": {
|
|
537
|
+
"beginAtZero": true,
|
|
538
|
+
"ticks": {
|
|
539
|
+
"color": "rgba(122, 122, 136, 1)",
|
|
540
|
+
"font": {
|
|
541
|
+
"size": 18
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
"grid": {
|
|
545
|
+
"color": "rgba(30, 30, 40, 1)"
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
"plugins": {
|
|
550
|
+
"legend": {
|
|
551
|
+
"position": "bottom",
|
|
552
|
+
"labels": {
|
|
553
|
+
"color": "rgba(200, 200, 210, 1)",
|
|
554
|
+
"font": {
|
|
555
|
+
"size": 18
|
|
556
|
+
},
|
|
557
|
+
"padding": 20
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
"layout": {
|
|
562
|
+
"padding": {
|
|
563
|
+
"bottom": 5
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
"type": "pageBreak"
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
"type": "section",
|
|
575
|
+
"comment": "Closing slide",
|
|
576
|
+
"content": [
|
|
577
|
+
{
|
|
578
|
+
"type": "spacer",
|
|
579
|
+
"mm": 24
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
"type": "text",
|
|
583
|
+
"label": "slide.kicker",
|
|
584
|
+
"text": "Get Started"
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
"type": "text",
|
|
588
|
+
"label": "slide.title",
|
|
589
|
+
"text": "npm install h17-sspdf"
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
"type": "spacer",
|
|
593
|
+
"mm": 8
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
"type": "divider",
|
|
597
|
+
"label": "slide.divider.accent",
|
|
598
|
+
"widthMm": 60
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
"type": "spacer",
|
|
602
|
+
"mm": 4
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
"type": "text",
|
|
606
|
+
"label": "slide.body",
|
|
607
|
+
"text": "Zero native dependencies. Works in Node.js. CLI and programmatic API included."
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
"type": "spacer",
|
|
611
|
+
"mm": 6
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
"type": "row",
|
|
615
|
+
"leftLabel": "slide.row.left",
|
|
616
|
+
"rightLabel": "slide.row.right",
|
|
617
|
+
"leftText": "GitHub",
|
|
618
|
+
"rightText": "github.com/hugopalma17/sspdf",
|
|
619
|
+
"xLeftMm": 24,
|
|
620
|
+
"xRightMm": 314
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
"type": "row",
|
|
624
|
+
"leftLabel": "slide.row.left",
|
|
625
|
+
"rightLabel": "slide.row.right",
|
|
626
|
+
"leftText": "Docs",
|
|
627
|
+
"rightText": "h17.ai/docs/sspdf",
|
|
628
|
+
"xLeftMm": 24,
|
|
629
|
+
"xRightMm": 314
|
|
630
|
+
}
|
|
631
|
+
]
|
|
632
|
+
}
|
|
633
|
+
]
|
|
634
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
const INTER = require("../../fonts/inter.js");
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
name: "Presentation Theme",
|
|
5
|
+
|
|
6
|
+
customFonts: [
|
|
7
|
+
{
|
|
8
|
+
family: "Inter",
|
|
9
|
+
faces: [
|
|
10
|
+
{ style: "normal", fileName: "Inter-Regular.ttf", data: INTER.Regular },
|
|
11
|
+
{ style: "bold", fileName: "Inter-Bold.ttf", data: INTER.Bold },
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
|
|
16
|
+
page: {
|
|
17
|
+
pageWidthMm: 338,
|
|
18
|
+
pageHeightMm: 190,
|
|
19
|
+
orientation: "landscape",
|
|
20
|
+
unit: "mm",
|
|
21
|
+
compress: true,
|
|
22
|
+
marginTopMm: 16,
|
|
23
|
+
marginBottomMm: 14,
|
|
24
|
+
marginLeftMm: 24,
|
|
25
|
+
marginRightMm: 24,
|
|
26
|
+
backgroundColor: [6, 6, 8],
|
|
27
|
+
defaultText: {
|
|
28
|
+
fontFamily: "helvetica",
|
|
29
|
+
fontStyle: "normal",
|
|
30
|
+
fontSize: 14,
|
|
31
|
+
color: [232, 232, 236],
|
|
32
|
+
lineHeight: 1.4,
|
|
33
|
+
},
|
|
34
|
+
defaultStroke: {
|
|
35
|
+
color: [232, 232, 236],
|
|
36
|
+
lineWidth: 0.2,
|
|
37
|
+
lineCap: "butt",
|
|
38
|
+
lineJoin: "miter",
|
|
39
|
+
},
|
|
40
|
+
defaultFillColor: [6, 6, 8],
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
layout: {
|
|
44
|
+
chartAlign: "center",
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
labels: {
|
|
48
|
+
// ─── Slide footer ─────────────────────────────────────────
|
|
49
|
+
"slide.footer.rule": {
|
|
50
|
+
color: [74, 74, 85],
|
|
51
|
+
lineWidth: 0.25,
|
|
52
|
+
marginBottomPx: 2,
|
|
53
|
+
},
|
|
54
|
+
"slide.footer.left": {
|
|
55
|
+
fontFamily: "Inter",
|
|
56
|
+
fontStyle: "normal",
|
|
57
|
+
fontSize: 8,
|
|
58
|
+
color: [122, 122, 136],
|
|
59
|
+
marginBottomPx: 0,
|
|
60
|
+
},
|
|
61
|
+
"slide.footer.right": {
|
|
62
|
+
fontFamily: "Inter",
|
|
63
|
+
fontStyle: "normal",
|
|
64
|
+
fontSize: 8,
|
|
65
|
+
color: [122, 122, 136],
|
|
66
|
+
marginBottomPx: 0,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// ─── Title slide ──────────────────────────────────────────
|
|
70
|
+
"slide.kicker": {
|
|
71
|
+
fontFamily: "Inter",
|
|
72
|
+
fontStyle: "bold",
|
|
73
|
+
fontSize: 11,
|
|
74
|
+
textTransform: "upper",
|
|
75
|
+
align: "center",
|
|
76
|
+
color: [0, 229, 255],
|
|
77
|
+
marginBottomPx: 6,
|
|
78
|
+
},
|
|
79
|
+
"slide.title": {
|
|
80
|
+
fontFamily: "Inter",
|
|
81
|
+
fontStyle: "bold",
|
|
82
|
+
fontSize: 36,
|
|
83
|
+
align: "center",
|
|
84
|
+
color: [232, 232, 236],
|
|
85
|
+
lineHeight: 1.15,
|
|
86
|
+
marginBottomPx: 8,
|
|
87
|
+
},
|
|
88
|
+
"slide.subtitle": {
|
|
89
|
+
fontFamily: "Inter",
|
|
90
|
+
fontStyle: "normal",
|
|
91
|
+
fontSize: 16,
|
|
92
|
+
align: "center",
|
|
93
|
+
color: [122, 122, 136],
|
|
94
|
+
lineHeight: 1.4,
|
|
95
|
+
marginBottomPx: 6,
|
|
96
|
+
},
|
|
97
|
+
"slide.author": {
|
|
98
|
+
fontFamily: "Inter",
|
|
99
|
+
fontStyle: "normal",
|
|
100
|
+
fontSize: 12,
|
|
101
|
+
align: "center",
|
|
102
|
+
color: [74, 74, 85],
|
|
103
|
+
marginBottomPx: 0,
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
// ─── Content slides ───────────────────────────────────────
|
|
107
|
+
"slide.heading": {
|
|
108
|
+
fontFamily: "Inter",
|
|
109
|
+
fontStyle: "bold",
|
|
110
|
+
fontSize: 24,
|
|
111
|
+
align: "center",
|
|
112
|
+
color: [232, 232, 236],
|
|
113
|
+
lineHeight: 1.2,
|
|
114
|
+
marginBottomPx: 10,
|
|
115
|
+
},
|
|
116
|
+
"slide.body": {
|
|
117
|
+
fontFamily: "Inter",
|
|
118
|
+
fontStyle: "normal",
|
|
119
|
+
fontSize: 14,
|
|
120
|
+
align: "center",
|
|
121
|
+
color: [122, 122, 136],
|
|
122
|
+
lineHeight: 1.5,
|
|
123
|
+
marginBottomPx: 6,
|
|
124
|
+
},
|
|
125
|
+
"slide.body.bold": {
|
|
126
|
+
fontFamily: "Inter",
|
|
127
|
+
fontStyle: "bold",
|
|
128
|
+
fontSize: 14,
|
|
129
|
+
align: "center",
|
|
130
|
+
color: [232, 232, 236],
|
|
131
|
+
lineHeight: 1.5,
|
|
132
|
+
marginBottomPx: 6,
|
|
133
|
+
},
|
|
134
|
+
"slide.caption": {
|
|
135
|
+
fontFamily: "Inter",
|
|
136
|
+
fontStyle: "normal",
|
|
137
|
+
fontSize: 10,
|
|
138
|
+
color: [74, 74, 85],
|
|
139
|
+
lineHeight: 1.3,
|
|
140
|
+
marginBottomPx: 4,
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// ─── Bullets ──────────────────────────────────────────────
|
|
144
|
+
"slide.bullet.marker": {
|
|
145
|
+
shape: "circle-filled",
|
|
146
|
+
shapeColor: [0, 229, 255],
|
|
147
|
+
shapeSize: 0.5,
|
|
148
|
+
textIndentMm: 3,
|
|
149
|
+
},
|
|
150
|
+
"slide.bullet.text": {
|
|
151
|
+
fontFamily: "Inter",
|
|
152
|
+
fontStyle: "normal",
|
|
153
|
+
fontSize: 14,
|
|
154
|
+
color: [122, 122, 136],
|
|
155
|
+
lineHeight: 1.5,
|
|
156
|
+
marginBottomPx: 5,
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// ─── Metrics / KPI ────────────────────────────────────────
|
|
160
|
+
"slide.metric.value": {
|
|
161
|
+
fontFamily: "Inter",
|
|
162
|
+
fontStyle: "bold",
|
|
163
|
+
fontSize: 28,
|
|
164
|
+
color: [0, 229, 255],
|
|
165
|
+
align: "center",
|
|
166
|
+
marginBottomPx: 2,
|
|
167
|
+
},
|
|
168
|
+
"slide.metric.label": {
|
|
169
|
+
fontFamily: "Inter",
|
|
170
|
+
fontStyle: "normal",
|
|
171
|
+
fontSize: 11,
|
|
172
|
+
color: [122, 122, 136],
|
|
173
|
+
align: "center",
|
|
174
|
+
marginBottomPx: 4,
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// ─── Dividers ─────────────────────────────────────────────
|
|
178
|
+
"slide.divider": {
|
|
179
|
+
color: [18, 18, 24],
|
|
180
|
+
lineWidth: 0.4,
|
|
181
|
+
marginTopPx: 6,
|
|
182
|
+
marginBottomPx: 6,
|
|
183
|
+
},
|
|
184
|
+
"slide.divider.accent": {
|
|
185
|
+
color: [0, 229, 255],
|
|
186
|
+
lineWidth: 1.2,
|
|
187
|
+
marginTopPx: 4,
|
|
188
|
+
marginBottomPx: 8,
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// ─── Callout box ──────────────────────────────────────────
|
|
192
|
+
"slide.callout": {
|
|
193
|
+
fontFamily: "Inter",
|
|
194
|
+
fontStyle: "normal",
|
|
195
|
+
fontSize: 13,
|
|
196
|
+
align: "center",
|
|
197
|
+
color: [0, 229, 255],
|
|
198
|
+
lineHeight: 1.45,
|
|
199
|
+
backgroundColor: [12, 12, 16],
|
|
200
|
+
borderColor: [0, 165, 184],
|
|
201
|
+
borderWidthMm: 0.35,
|
|
202
|
+
borderRadiusMm: 2,
|
|
203
|
+
paddingTopMm: 4,
|
|
204
|
+
paddingBottomMm: 4,
|
|
205
|
+
paddingLeftMm: 5,
|
|
206
|
+
paddingRightMm: 5,
|
|
207
|
+
marginTopPx: 4,
|
|
208
|
+
marginBottomPx: 6,
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// ─── Image ────────────────────────────────────────────────
|
|
212
|
+
"slide.image": {
|
|
213
|
+
paddingTopMm: 2,
|
|
214
|
+
paddingBottomMm: 2,
|
|
215
|
+
},
|
|
216
|
+
"slide.image.caption": {
|
|
217
|
+
fontFamily: "Inter",
|
|
218
|
+
fontStyle: "normal",
|
|
219
|
+
fontSize: 9,
|
|
220
|
+
color: [74, 74, 85],
|
|
221
|
+
align: "center",
|
|
222
|
+
lineHeight: 1.3,
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// ─── Table ────────────────────────────────────────────────
|
|
226
|
+
"slide.table": {
|
|
227
|
+
fontFamily: "Inter",
|
|
228
|
+
fontStyle: "normal",
|
|
229
|
+
fontSize: 11,
|
|
230
|
+
color: [200, 200, 210],
|
|
231
|
+
lineHeight: 1.3,
|
|
232
|
+
cellPaddingMm: 2.5,
|
|
233
|
+
borderColor: [30, 30, 40],
|
|
234
|
+
borderTopMm: 0,
|
|
235
|
+
borderBottomMm: 0.15,
|
|
236
|
+
borderLeftMm: 0,
|
|
237
|
+
borderRightMm: 0,
|
|
238
|
+
altRowColor: [12, 12, 16],
|
|
239
|
+
},
|
|
240
|
+
"slide.table.header": {
|
|
241
|
+
fontFamily: "Inter",
|
|
242
|
+
fontStyle: "bold",
|
|
243
|
+
fontSize: 11,
|
|
244
|
+
color: [6, 6, 8],
|
|
245
|
+
lineHeight: 1.3,
|
|
246
|
+
cellPaddingMm: 2.5,
|
|
247
|
+
backgroundColor: [0, 229, 255],
|
|
248
|
+
borderColor: [0, 200, 220],
|
|
249
|
+
borderTopMm: 0,
|
|
250
|
+
borderBottomMm: 0.3,
|
|
251
|
+
borderLeftMm: 0,
|
|
252
|
+
borderRightMm: 0,
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
// ─── Row labels ───────────────────────────────────────────
|
|
256
|
+
"slide.row.left": {
|
|
257
|
+
fontFamily: "Inter",
|
|
258
|
+
fontStyle: "bold",
|
|
259
|
+
fontSize: 12,
|
|
260
|
+
color: [232, 232, 236],
|
|
261
|
+
marginBottomPx: 2,
|
|
262
|
+
},
|
|
263
|
+
"slide.row.right": {
|
|
264
|
+
fontFamily: "Inter",
|
|
265
|
+
fontStyle: "normal",
|
|
266
|
+
fontSize: 12,
|
|
267
|
+
color: [122, 122, 136],
|
|
268
|
+
marginBottomPx: 2,
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
// ─── Spacers ──────────────────────────────────────────────
|
|
272
|
+
"slide.spacer.sm": { spaceMm: 4 },
|
|
273
|
+
"slide.spacer.md": { spaceMm: 8 },
|
|
274
|
+
"slide.spacer.lg": { spaceMm: 16 },
|
|
275
|
+
},
|
|
276
|
+
};
|