@heylemon/lemonade 0.1.6 → 0.1.7

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.
@@ -0,0 +1,624 @@
1
+ # PptxGenJS API Tutorial
2
+
3
+ PptxGenJS is a JavaScript library for generating PowerPoint presentations programmatically. This guide covers the most practical patterns for building professional slides.
4
+
5
+ ## Setup & Basic Structure
6
+
7
+ Create a new presentation and configure it:
8
+
9
+ ```javascript
10
+ const PptxGenJS = require("pptxgenjs");
11
+ const pres = new PptxGenJS();
12
+
13
+ pres.layout = "LAYOUT_16x9";
14
+ pres.author = "Your Name";
15
+ pres.title = "Presentation Title";
16
+ pres.subject = "Subtitle or description";
17
+
18
+ // Add slides
19
+ const slide1 = pres.addSlide();
20
+
21
+ // Save to file
22
+ pres.writeFile({ fileName: "output.pptx" });
23
+ ```
24
+
25
+ Key point: Always create a fresh `PptxGenJS()` instance per presentation. Don't reuse across multiple presentations.
26
+
27
+ ## Layout Dimensions
28
+
29
+ Standard 16:9 layout is 10 inches wide by 5.625 inches tall. All positioning is in inches.
30
+
31
+ ```javascript
32
+ // Slide area
33
+ const W = 10; // Width
34
+ const H = 5.625; // Height
35
+ const M = 0.5; // Margin (standard)
36
+ const contentWidth = W - 2 * M; // 9 inches
37
+
38
+ // Common positioning
39
+ const titleY = M; // Top margin
40
+ const contentStartY = M + 0.8; // After title
41
+ const bottomY = H - 0.8; // Near bottom for footer
42
+ ```
43
+
44
+ ## Text & Formatting
45
+
46
+ ### Basic Text
47
+
48
+ ```javascript
49
+ slide.addText("Hello World", {
50
+ x: 0.5, y: 1, w: 9, h: 0.5,
51
+ fontSize: 16,
52
+ fontFace: "Arial",
53
+ color: "000000",
54
+ bold: true,
55
+ align: "left",
56
+ valign: "top"
57
+ });
58
+ ```
59
+
60
+ ### Multi-line Text with Rich Formatting
61
+
62
+ Use an array to mix formatted segments:
63
+
64
+ ```javascript
65
+ slide.addText([
66
+ { text: "This is bold", options: { bold: true } },
67
+ { text: " and this is ", options: {} },
68
+ { text: "italic", options: { italic: true } }
69
+ ], {
70
+ x: 0.5, y: 2, w: 9, h: 1,
71
+ fontSize: 14,
72
+ fontFace: "Arial",
73
+ color: "1E293B",
74
+ align: "left"
75
+ });
76
+ ```
77
+
78
+ ### Bullets & Line Breaks
79
+
80
+ Always use `bullet: true` with `breakLine: true` between items:
81
+
82
+ ```javascript
83
+ slide.addText([
84
+ { text: "First point", options: { bullet: true, breakLine: true } },
85
+ { text: "Second point", options: { bullet: true, breakLine: true } },
86
+ { text: "Third point", options: { bullet: true, breakLine: false } } // Last item
87
+ ], {
88
+ x: 0.7, y: 2, w: 8.6, h: 2,
89
+ fontSize: 14,
90
+ fontFace: "Arial",
91
+ color: "1E293B",
92
+ paraSpaceAfter: 6, // Space after each paragraph
93
+ valign: "top"
94
+ });
95
+ ```
96
+
97
+ **Never use Unicode bullets** like `•` or `◦`. Always use `bullet: true`.
98
+
99
+ ### Text Block with Padding
100
+
101
+ Use `margin` to add internal padding:
102
+
103
+ ```javascript
104
+ slide.addText("This text has padding", {
105
+ x: 1, y: 1, w: 8, h: 2,
106
+ fontSize: 14,
107
+ margin: [0.2, 0.3, 0.2, 0.3], // [top, right, bottom, left]
108
+ align: "left"
109
+ });
110
+ ```
111
+
112
+ ## Shapes
113
+
114
+ ### Rectangle
115
+
116
+ ```javascript
117
+ slide.addShape(pres.shapes.RECTANGLE, {
118
+ x: 1, y: 1, w: 4, h: 2,
119
+ fill: { color: "E74C3C" },
120
+ line: { color: "C0392B", width: 2 }, // Border
121
+ shadow: {
122
+ type: "outer",
123
+ color: "000000",
124
+ blur: 6,
125
+ offset: 2,
126
+ angle: 135,
127
+ opacity: 0.1
128
+ }
129
+ });
130
+ ```
131
+
132
+ ### Oval / Circle
133
+
134
+ ```javascript
135
+ slide.addShape(pres.shapes.OVAL, {
136
+ x: 1, y: 1, w: 1.5, h: 1.5, // Width and height equal = circle
137
+ fill: { color: "F5A623" }
138
+ });
139
+ ```
140
+
141
+ ### Rounded Rectangle
142
+
143
+ ```javascript
144
+ slide.addShape(pres.shapes.ROUNDED_RECTANGLE, {
145
+ x: 1, y: 1, w: 4, h: 2,
146
+ fill: { color: "FFFFFF" },
147
+ rectRadius: 0.1, // Corner radius in inches
148
+ shadow: { type: "outer", color: "000000", blur: 4, offset: 1, angle: 135, opacity: 0.08 }
149
+ });
150
+ ```
151
+
152
+ ### Line
153
+
154
+ ```javascript
155
+ slide.addShape(pres.shapes.LINE, {
156
+ x: 1, y: 2, w: 4, h: 0, // Height 0 for horizontal line
157
+ line: { color: "F5A623", width: 3 }
158
+ });
159
+ ```
160
+
161
+ ### Shape Properties Reference
162
+
163
+ | Property | Type | Notes |
164
+ |---|---|---|
165
+ | `x, y, w, h` | number | Position (inches) and size |
166
+ | `fill` | object | `{ color: "HEX" }` or `{ type: "solid", color: "HEX", transparency: 0 }` |
167
+ | `line` | object | `{ color: "HEX", width: 2, dashType: "solid" \| "dash" }` |
168
+ | `shadow` | object | `{ type: "outer", color, blur, offset, angle, opacity }` |
169
+ | `transparency` | number | 0-100. Use `fill.transparency`, not color string. |
170
+ | `rotate` | number | Degrees (0-360) |
171
+
172
+ ## Images
173
+
174
+ ### From File Path
175
+
176
+ ```javascript
177
+ slide.addImage({
178
+ path: "/path/to/image.jpg",
179
+ x: 5, y: 1, w: 4, h: 2.5
180
+ });
181
+ ```
182
+
183
+ ### From URL
184
+
185
+ ```javascript
186
+ slide.addImage({
187
+ url: "https://example.com/logo.png",
188
+ x: 5, y: 1, w: 4, h: 2.5
189
+ });
190
+ ```
191
+
192
+ ### From Base64
193
+
194
+ ```javascript
195
+ const base64Data = "data:image/png;base64,iVBORw0KGgo...";
196
+ slide.addImage({
197
+ data: base64Data,
198
+ x: 5, y: 1, w: 4, h: 2.5
199
+ });
200
+ ```
201
+
202
+ ### Sizing Modes
203
+
204
+ ```javascript
205
+ slide.addImage({
206
+ path: "/image.jpg",
207
+ x: 1, y: 1, w: 4, h: 2.5,
208
+ sizing: {
209
+ type: "cover", // "cover" = crop to fill, "contain" = fit inside
210
+ w: 4,
211
+ h: 2.5
212
+ }
213
+ });
214
+ ```
215
+
216
+ ## Icons
217
+
218
+ Convert React Icons to base64 PNG and embed:
219
+
220
+ ```javascript
221
+ // 1. Generate SVG from react-icons, save as .svg
222
+ // 2. Convert to PNG with sharp: sharp('icon.svg').png().toFile('icon.png')
223
+ // 3. Convert PNG to base64
224
+ const fs = require('fs');
225
+ const iconBase64 = fs.readFileSync('icon.png', 'base64');
226
+ const iconDataUri = `data:image/png;base64,${iconBase64}`;
227
+
228
+ // 4. Add to slide
229
+ slide.addShape(pres.shapes.OVAL, {
230
+ x: 1, y: 1, w: 0.4, h: 0.4,
231
+ fill: { color: "F5A623" }
232
+ });
233
+ slide.addImage({
234
+ data: iconDataUri,
235
+ x: 1.05, y: 1.05, w: 0.3, h: 0.3
236
+ });
237
+ ```
238
+
239
+ ## Slide Backgrounds
240
+
241
+ ### Solid Color
242
+
243
+ ```javascript
244
+ const slide = pres.addSlide();
245
+ slide.background = { color: "1E2761" };
246
+ ```
247
+
248
+ ### Gradient
249
+
250
+ ```javascript
251
+ slide.background = {
252
+ type: "solid",
253
+ fill: "F8FAFC"
254
+ };
255
+ ```
256
+
257
+ ## Tables
258
+
259
+ ### Basic Table
260
+
261
+ ```javascript
262
+ const tableData = [
263
+ [
264
+ { text: "Header A", options: { bold: true, color: "FFFFFF", fill: { color: "1E2761" } } },
265
+ { text: "Header B", options: { bold: true, color: "FFFFFF", fill: { color: "1E2761" } } }
266
+ ],
267
+ [
268
+ { text: "Row 1, Col A", options: {} },
269
+ { text: "Row 1, Col B", options: { fill: { color: "F8FAFC" } } }
270
+ ],
271
+ [
272
+ { text: "Row 2, Col A", options: { fill: { color: "F8FAFC" } } },
273
+ { text: "Row 2, Col B", options: {} }
274
+ ]
275
+ ];
276
+
277
+ slide.addTable(tableData, {
278
+ x: 0.5, y: 2, w: 9, h: 2,
279
+ colW: [4.5, 4.5], // Column widths
280
+ border: { pt: 1, color: "E2E8F0" },
281
+ fontFace: "Arial",
282
+ fontSize: 12
283
+ });
284
+ ```
285
+
286
+ ### Table with Merged Cells
287
+
288
+ ```javascript
289
+ const tableData = [
290
+ [
291
+ { text: "Merged", options: {}, colspan: 2 }, // Spans 2 columns
292
+ { text: "Normal", options: {} }
293
+ ]
294
+ ];
295
+ ```
296
+
297
+ ## Charts
298
+
299
+ ### Bar Chart
300
+
301
+ ```javascript
302
+ const barData = [
303
+ {
304
+ name: "Series 1",
305
+ labels: ["Q1", "Q2", "Q3", "Q4"],
306
+ values: [15, 22, 18, 25]
307
+ },
308
+ {
309
+ name: "Series 2",
310
+ labels: ["Q1", "Q2", "Q3", "Q4"],
311
+ values: [12, 20, 16, 23]
312
+ }
313
+ ];
314
+
315
+ slide.addChart(pres.ChartTypes.bar, barData, {
316
+ x: 1, y: 1, w: 8, h: 3,
317
+ chartColors: ["2E86AB", "F5A623"],
318
+ showLegend: true,
319
+ dataLabelPosition: "outEnd" // Outside bars
320
+ });
321
+ ```
322
+
323
+ ### Line Chart
324
+
325
+ ```javascript
326
+ const lineData = [
327
+ {
328
+ name: "Revenue",
329
+ labels: ["Jan", "Feb", "Mar", "Apr"],
330
+ values: [100, 120, 140, 160]
331
+ }
332
+ ];
333
+
334
+ slide.addChart(pres.ChartTypes.line, lineData, {
335
+ x: 1, y: 1, w: 8, h: 3,
336
+ lineSize: 2,
337
+ chartColors: ["F5A623"],
338
+ dataLabelPosition: "outEnd"
339
+ });
340
+ ```
341
+
342
+ ### Pie Chart
343
+
344
+ ```javascript
345
+ const pieData = [
346
+ {
347
+ name: "Category",
348
+ labels: ["Segment A", "Segment B", "Segment C"],
349
+ values: [30, 25, 45]
350
+ }
351
+ ];
352
+
353
+ slide.addChart(pres.ChartTypes.pie, pieData, {
354
+ x: 1, y: 1, w: 4, h: 3,
355
+ chartColors: ["2E86AB", "F5A623", "E74C3C"],
356
+ dataLabelPosition: "ctr" // Center
357
+ });
358
+ ```
359
+
360
+ ### Chart Options
361
+
362
+ | Option | Values | Notes |
363
+ |---|---|---|
364
+ | `chartColors` | Array of hex colors | Controls bar/line/pie segment colors |
365
+ | `dataLabelPosition` | `"outEnd"`, `"ctr"`, `"inEnd"`, `"ctrH"` | Where labels appear |
366
+ | `dataLabelFontSize` | number | Font size of data labels |
367
+ | `gridLines` | object | `{ style: "solid", color: "E0E0E0", size: 1 }` |
368
+ | `showLegend` | boolean | Show legend on chart |
369
+ | `legendPos` | `"b"`, `"r"`, `"t"`, `"l"` | Legend position (bottom, right, top, left) |
370
+
371
+ ## Slide Masters
372
+
373
+ Define a master slide template and reuse it:
374
+
375
+ ```javascript
376
+ // Define master
377
+ pres.defineLayout({ name: "MASTER_CONTENT" }, (slide) => {
378
+ slide.background = { color: "F8FAFC" };
379
+
380
+ // Add a footer
381
+ slide.addText("© 2024 Company", {
382
+ x: 0.5, y: H - 0.4, w: 9, h: 0.3,
383
+ fontSize: 8,
384
+ color: "64748B",
385
+ align: "right"
386
+ });
387
+ });
388
+
389
+ // Use master
390
+ const slide = pres.addSlide("MASTER_CONTENT");
391
+ ```
392
+
393
+ ## Common Pitfalls
394
+
395
+ ### Colors
396
+
397
+ - **NEVER use `#` with hex colors** — `"#FF0000"` corrupts the file. Always use `"FF0000"`.
398
+ - **NEVER encode opacity in hex** — Don't try `"FF0000CC"`. Use `fill: { color: "FF0000", transparency: 50 }` instead.
399
+
400
+ ### Bullets
401
+
402
+ - **Always use `bullet: true`** — Never try to build bullets manually with Unicode bullets.
403
+ - **Always use `breakLine: true` between items** — This is required for proper spacing.
404
+ - **Avoid `lineSpacing` with bullets** — Use `paraSpaceAfter: 6` instead for better control.
405
+
406
+ ### Objects & Mutations
407
+
408
+ - **Create fresh option objects for each call** — PptxGenJS mutates option objects. Reusing the same object across multiple `addShape` or `addText` calls causes unexpected behavior.
409
+
410
+ ```javascript
411
+ // WRONG: Shadow object reused
412
+ const shadow = { type: "outer", color: "000000", blur: 6, offset: 2, opacity: 0.1 };
413
+ slide.addShape(pres.shapes.RECTANGLE, { x: 1, y: 1, w: 2, h: 1, shadow });
414
+ slide.addShape(pres.shapes.RECTANGLE, { x: 4, y: 1, w: 2, h: 1, shadow });
415
+
416
+ // RIGHT: Fresh shadow for each shape
417
+ slide.addShape(pres.shapes.RECTANGLE, {
418
+ x: 1, y: 1, w: 2, h: 1,
419
+ shadow: { type: "outer", color: "000000", blur: 6, offset: 2, opacity: 0.1 }
420
+ });
421
+ slide.addShape(pres.shapes.RECTANGLE, {
422
+ x: 4, y: 1, w: 2, h: 1,
423
+ shadow: { type: "outer", color: "000000", blur: 6, offset: 2, opacity: 0.1 }
424
+ });
425
+ ```
426
+
427
+ ### Shadow Offsets
428
+
429
+ - **Shadow `offset` must be non-negative** — Negative values cause file corruption. Use positive values only.
430
+ - **Typical shadow: blur 6, offset 2, opacity 0.08-0.1** — This creates a subtle drop shadow.
431
+
432
+ ### Rounded Rectangles
433
+
434
+ - **Don't pair rounded rectangles with accent borders** — Rounded rectangles with thick borders (> 2pt) look awkward. Use subtle shadows instead.
435
+
436
+ ## Quick Reference
437
+
438
+ ### Shape Types
439
+ ```javascript
440
+ pres.shapes.RECTANGLE
441
+ pres.shapes.OVAL
442
+ pres.shapes.LINE
443
+ pres.shapes.ROUNDED_RECTANGLE
444
+ pres.shapes.DIAMOND
445
+ pres.shapes.TRIANGLE
446
+ ```
447
+
448
+ ### Chart Types
449
+ ```javascript
450
+ pres.ChartTypes.bar
451
+ pres.ChartTypes.line
452
+ pres.ChartTypes.pie
453
+ pres.ChartTypes.doughnut
454
+ pres.ChartTypes.area
455
+ pres.ChartTypes.scatter
456
+ ```
457
+
458
+ ### Text Alignment
459
+ ```javascript
460
+ align: "left" | "center" | "right"
461
+ valign: "top" | "middle" | "bottom"
462
+ ```
463
+
464
+ ### Common Font Faces
465
+ ```javascript
466
+ "Arial"
467
+ "Arial Black"
468
+ "Calibri"
469
+ "Georgia"
470
+ "Trebuchet MS"
471
+ "Helvetica"
472
+ ```
473
+
474
+ ### Sizing Guidelines
475
+
476
+ | Element | Size (pt) |
477
+ |---|---|
478
+ | Slide title | 20-28 |
479
+ | Section heading | 18 |
480
+ | Body text | 14 |
481
+ | Bullet text | 14 |
482
+ | Captions | 10-11 |
483
+ | Stat numbers | 60 |
484
+ | Stat labels | 12 |
485
+ | Table header | 11-12 |
486
+ | Table body | 11 |
487
+
488
+ ### Spacing Guidelines
489
+
490
+ | Element | Value |
491
+ |---|---|
492
+ | Slide margins | 0.5" |
493
+ | Content block gap | 0.3-0.5" |
494
+ | Line height multiplier | 1.3 |
495
+ | Bullet spacing | `paraSpaceAfter: 6` |
496
+ | Card shadow blur | 6 |
497
+ | Card shadow offset | 2 |
498
+ | Card shadow opacity | 0.08-0.1 |
499
+ | Accent bar height | 0.06" |
500
+
501
+ ## Pattern: Professional Stat Card
502
+
503
+ ```javascript
504
+ // Background card
505
+ slide.addShape(pres.shapes.RECTANGLE, {
506
+ x: 1, y: 1, w: 2.5, h: 2,
507
+ fill: { color: "FFFFFF" },
508
+ shadow: { type: "outer", color: "000000", blur: 6, offset: 2, opacity: 0.08 }
509
+ });
510
+
511
+ // Color bar at top
512
+ slide.addShape(pres.shapes.RECTANGLE, {
513
+ x: 1, y: 1, w: 2.5, h: 0.06,
514
+ fill: { color: "F5A623" }
515
+ });
516
+
517
+ // Large number
518
+ slide.addText("3x", {
519
+ x: 1, y: 1.4, w: 2.5, h: 0.8,
520
+ fontSize: 60,
521
+ fontFace: "Arial Black",
522
+ color: "F5A623",
523
+ bold: true,
524
+ align: "center"
525
+ });
526
+
527
+ // Label
528
+ slide.addText("User Growth", {
529
+ x: 1.2, y: 2.3, w: 2.1, h: 0.5,
530
+ fontSize: 12,
531
+ fontFace: "Arial",
532
+ color: "64748B",
533
+ align: "center"
534
+ });
535
+ ```
536
+
537
+ ## Pattern: Two-Column Layout
538
+
539
+ ```javascript
540
+ const colW = (9 - 0.3) / 2; // Two equal columns
541
+
542
+ // Left column
543
+ slide.addText([
544
+ { text: "Point 1", options: { bullet: true, breakLine: true } },
545
+ { text: "Point 2", options: { bullet: true, breakLine: true } },
546
+ { text: "Point 3", options: { bullet: true, breakLine: false } }
547
+ ], {
548
+ x: 0.5, y: 1.5, w: colW, h: 3,
549
+ fontSize: 14,
550
+ fontFace: "Arial",
551
+ color: "1E293B",
552
+ valign: "top",
553
+ paraSpaceAfter: 6
554
+ });
555
+
556
+ // Right column
557
+ slide.addText([
558
+ { text: "Benefit 1", options: { bullet: true, breakLine: true } },
559
+ { text: "Benefit 2", options: { bullet: true, breakLine: true } },
560
+ { text: "Benefit 3", options: { bullet: true, breakLine: false } }
561
+ ], {
562
+ x: 0.5 + colW + 0.3, y: 1.5, w: colW, h: 3,
563
+ fontSize: 14,
564
+ fontFace: "Arial",
565
+ color: "1E293B",
566
+ valign: "top",
567
+ paraSpaceAfter: 6
568
+ });
569
+ ```
570
+
571
+ ## Example: Complete Slide
572
+
573
+ ```javascript
574
+ const PptxGenJS = require("pptxgenjs");
575
+ const pres = new PptxGenJS();
576
+ pres.layout = "LAYOUT_16x9";
577
+ pres.author = "Jane Doe";
578
+ pres.title = "Q4 Results";
579
+
580
+ const slide = pres.addSlide();
581
+ const W = 10, H = 5.625, M = 0.5, CW = 9;
582
+
583
+ // Background
584
+ slide.background = { color: "F8FAFC" };
585
+
586
+ // Title
587
+ slide.addText("Revenue Beat Target by 15%", {
588
+ x: M, y: M, w: CW, h: 0.6,
589
+ fontSize: 20,
590
+ fontFace: "Arial Black",
591
+ color: "1E293B",
592
+ bold: true
593
+ });
594
+
595
+ // Accent underline
596
+ slide.addShape(pres.shapes.RECTANGLE, {
597
+ x: M, y: M + 0.6, w: 0.8, h: 0.04,
598
+ fill: { color: "F5A623" }
599
+ });
600
+
601
+ // Body text
602
+ slide.addText("We exceeded quarterly targets across all regions.", {
603
+ x: M, y: M + 1, w: CW, h: 0.6,
604
+ fontSize: 14,
605
+ fontFace: "Arial",
606
+ color: "1E293B"
607
+ });
608
+
609
+ // Bullet points
610
+ slide.addText([
611
+ { text: "Sales team landed 3 enterprise deals", options: { bullet: true, breakLine: true } },
612
+ { text: "Product revenue grew 22% MoM", options: { bullet: true, breakLine: true } },
613
+ { text: "Customer retention at 96%", options: { bullet: true, breakLine: false } }
614
+ ], {
615
+ x: M + 0.2, y: M + 1.8, w: CW - 0.4, h: 2,
616
+ fontSize: 14,
617
+ fontFace: "Arial",
618
+ color: "1E293B",
619
+ paraSpaceAfter: 6,
620
+ valign: "top"
621
+ });
622
+
623
+ pres.writeFile({ fileName: "output.pptx" });
624
+ ```