quicklook-pptx-renderer 0.1.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +175 -0
  5. package/dist/diff/compare.d.ts +17 -0
  6. package/dist/diff/compare.js +71 -0
  7. package/dist/index.d.ts +29 -0
  8. package/dist/index.js +72 -0
  9. package/dist/lint.d.ts +27 -0
  10. package/dist/lint.js +328 -0
  11. package/dist/mapper/bleed-map.d.ts +6 -0
  12. package/dist/mapper/bleed-map.js +1 -0
  13. package/dist/mapper/constants.d.ts +2 -0
  14. package/dist/mapper/constants.js +4 -0
  15. package/dist/mapper/drawable-mapper.d.ts +16 -0
  16. package/dist/mapper/drawable-mapper.js +1464 -0
  17. package/dist/mapper/html-generator.d.ts +13 -0
  18. package/dist/mapper/html-generator.js +539 -0
  19. package/dist/mapper/image-mapper.d.ts +14 -0
  20. package/dist/mapper/image-mapper.js +70 -0
  21. package/dist/mapper/nano-malloc.d.ts +130 -0
  22. package/dist/mapper/nano-malloc.js +197 -0
  23. package/dist/mapper/ql-bleed.d.ts +35 -0
  24. package/dist/mapper/ql-bleed.js +254 -0
  25. package/dist/mapper/shape-mapper.d.ts +41 -0
  26. package/dist/mapper/shape-mapper.js +2384 -0
  27. package/dist/mapper/slide-mapper.d.ts +4 -0
  28. package/dist/mapper/slide-mapper.js +112 -0
  29. package/dist/mapper/style-builder.d.ts +12 -0
  30. package/dist/mapper/style-builder.js +30 -0
  31. package/dist/mapper/text-mapper.d.ts +14 -0
  32. package/dist/mapper/text-mapper.js +302 -0
  33. package/dist/model/enums.d.ts +25 -0
  34. package/dist/model/enums.js +2 -0
  35. package/dist/model/types.d.ts +482 -0
  36. package/dist/model/types.js +7 -0
  37. package/dist/package/content-types.d.ts +1 -0
  38. package/dist/package/content-types.js +4 -0
  39. package/dist/package/package.d.ts +10 -0
  40. package/dist/package/package.js +52 -0
  41. package/dist/package/relationships.d.ts +6 -0
  42. package/dist/package/relationships.js +25 -0
  43. package/dist/package/zip.d.ts +6 -0
  44. package/dist/package/zip.js +17 -0
  45. package/dist/reader/color.d.ts +3 -0
  46. package/dist/reader/color.js +79 -0
  47. package/dist/reader/drawing.d.ts +17 -0
  48. package/dist/reader/drawing.js +403 -0
  49. package/dist/reader/effects.d.ts +2 -0
  50. package/dist/reader/effects.js +83 -0
  51. package/dist/reader/fill.d.ts +2 -0
  52. package/dist/reader/fill.js +94 -0
  53. package/dist/reader/presentation.d.ts +5 -0
  54. package/dist/reader/presentation.js +127 -0
  55. package/dist/reader/slide-layout.d.ts +2 -0
  56. package/dist/reader/slide-layout.js +28 -0
  57. package/dist/reader/slide-master.d.ts +4 -0
  58. package/dist/reader/slide-master.js +49 -0
  59. package/dist/reader/slide.d.ts +2 -0
  60. package/dist/reader/slide.js +26 -0
  61. package/dist/reader/text-list-style.d.ts +2 -0
  62. package/dist/reader/text-list-style.js +9 -0
  63. package/dist/reader/text.d.ts +5 -0
  64. package/dist/reader/text.js +295 -0
  65. package/dist/reader/theme.d.ts +2 -0
  66. package/dist/reader/theme.js +109 -0
  67. package/dist/reader/transform.d.ts +2 -0
  68. package/dist/reader/transform.js +21 -0
  69. package/dist/render/image-renderer.d.ts +3 -0
  70. package/dist/render/image-renderer.js +33 -0
  71. package/dist/render/renderer.d.ts +9 -0
  72. package/dist/render/renderer.js +178 -0
  73. package/dist/render/shape-renderer.d.ts +3 -0
  74. package/dist/render/shape-renderer.js +175 -0
  75. package/dist/render/text-renderer.d.ts +3 -0
  76. package/dist/render/text-renderer.js +152 -0
  77. package/dist/resolve/color-resolver.d.ts +18 -0
  78. package/dist/resolve/color-resolver.js +321 -0
  79. package/dist/resolve/font-map.d.ts +2 -0
  80. package/dist/resolve/font-map.js +66 -0
  81. package/dist/resolve/inheritance.d.ts +5 -0
  82. package/dist/resolve/inheritance.js +106 -0
  83. package/package.json +74 -0
@@ -0,0 +1,321 @@
1
+ // ── Public API ──────────────────────────────────────────────────────
2
+ export function resolveColor(color, colorMap, colorScheme) {
3
+ let base;
4
+ switch (color.type) {
5
+ case "rgb":
6
+ base = { r: color.r, g: color.g, b: color.b, a: 1 };
7
+ break;
8
+ case "scRgb":
9
+ base = {
10
+ r: Math.round((color.r / 100000) * 255),
11
+ g: Math.round((color.g / 100000) * 255),
12
+ b: Math.round((color.b / 100000) * 255),
13
+ a: 1,
14
+ };
15
+ break;
16
+ case "hsl":
17
+ base = { ...hslToRgb(color.hue / 60000, color.sat / 100000, color.lum / 100000), a: 1 };
18
+ break;
19
+ case "preset":
20
+ base = { ...resolvePreset(color.val), a: 1 };
21
+ break;
22
+ case "system":
23
+ base = { ...resolveSystem(color.val, color.lastClr), a: 1 };
24
+ break;
25
+ case "scheme": {
26
+ const mapped = resolveSchemeToBase(color.val, colorMap, colorScheme);
27
+ base = resolveColor(mapped, colorMap, colorScheme);
28
+ break;
29
+ }
30
+ }
31
+ return applyTransforms(base, color.transforms);
32
+ }
33
+ // ── Scheme resolution ───────────────────────────────────────────────
34
+ const COLOR_MAP_KEYS = new Set(["bg1", "tx1", "bg2", "tx2"]);
35
+ function resolveSchemeToBase(val, colorMap, colorScheme) {
36
+ const schemeName = COLOR_MAP_KEYS.has(val)
37
+ ? colorMap.mappings[val]
38
+ : val;
39
+ return colorScheme.colors[schemeName] ?? { type: "rgb", r: 0, g: 0, b: 0 };
40
+ }
41
+ // ── System colors ───────────────────────────────────────────────────
42
+ const SYSTEM_COLORS = {
43
+ windowText: { r: 0, g: 0, b: 0 },
44
+ window: { r: 255, g: 255, b: 255 },
45
+ highlightText: { r: 255, g: 255, b: 255 },
46
+ highlight: { r: 0, g: 120, b: 215 },
47
+ grayText: { r: 128, g: 128, b: 128 },
48
+ btnFace: { r: 240, g: 240, b: 240 },
49
+ btnText: { r: 0, g: 0, b: 0 },
50
+ captionText: { r: 0, g: 0, b: 0 },
51
+ menuText: { r: 0, g: 0, b: 0 },
52
+ infoText: { r: 0, g: 0, b: 0 },
53
+ infoBk: { r: 255, g: 255, b: 225 },
54
+ btnHighlight: { r: 255, g: 255, b: 255 },
55
+ "3dDkShadow": { r: 105, g: 105, b: 105 },
56
+ "3dLight": { r: 227, g: 227, b: 227 },
57
+ activeBorder: { r: 180, g: 180, b: 180 },
58
+ activeCaption: { r: 153, g: 180, b: 209 },
59
+ appWorkspace: { r: 171, g: 171, b: 171 },
60
+ background: { r: 0, g: 0, b: 0 },
61
+ btnShadow: { r: 160, g: 160, b: 160 },
62
+ inactiveCaption: { r: 191, g: 205, b: 219 },
63
+ inactiveBorder: { r: 244, g: 247, b: 252 },
64
+ inactiveCaptionText: { r: 0, g: 0, b: 0 },
65
+ menu: { r: 240, g: 240, b: 240 },
66
+ menuBar: { r: 240, g: 240, b: 240 },
67
+ menuHighlight: { r: 0, g: 120, b: 215 },
68
+ scrollBar: { r: 200, g: 200, b: 200 },
69
+ windowFrame: { r: 100, g: 100, b: 100 },
70
+ };
71
+ function parseHex(hex) {
72
+ const h = hex.replace(/^#/, "");
73
+ return {
74
+ r: parseInt(h.slice(0, 2), 16),
75
+ g: parseInt(h.slice(2, 4), 16),
76
+ b: parseInt(h.slice(4, 6), 16),
77
+ };
78
+ }
79
+ function resolveSystem(val, lastClr) {
80
+ return SYSTEM_COLORS[val] ?? (lastClr ? parseHex(lastClr) : { r: 0, g: 0, b: 0 });
81
+ }
82
+ // ── Preset colors ───────────────────────────────────────────────────
83
+ const PRESET_COLORS = {
84
+ aliceBlue: { r: 240, g: 248, b: 255 }, antiqueWhite: { r: 250, g: 235, b: 215 },
85
+ aqua: { r: 0, g: 255, b: 255 }, aquamarine: { r: 127, g: 255, b: 212 },
86
+ azure: { r: 240, g: 255, b: 255 }, beige: { r: 245, g: 245, b: 220 },
87
+ bisque: { r: 255, g: 228, b: 196 }, black: { r: 0, g: 0, b: 0 },
88
+ blanchedAlmond: { r: 255, g: 235, b: 205 }, blue: { r: 0, g: 0, b: 255 },
89
+ blueViolet: { r: 138, g: 43, b: 226 }, brown: { r: 165, g: 42, b: 42 },
90
+ burlyWood: { r: 222, g: 184, b: 135 }, cadetBlue: { r: 95, g: 158, b: 160 },
91
+ chartreuse: { r: 127, g: 255, b: 0 }, chocolate: { r: 210, g: 105, b: 30 },
92
+ coral: { r: 255, g: 127, b: 80 }, cornflowerBlue: { r: 100, g: 149, b: 237 },
93
+ cornsilk: { r: 255, g: 248, b: 220 }, crimson: { r: 220, g: 20, b: 60 },
94
+ cyan: { r: 0, g: 255, b: 255 }, dkBlue: { r: 0, g: 0, b: 139 },
95
+ dkCyan: { r: 0, g: 139, b: 139 }, dkGoldenrod: { r: 184, g: 134, b: 11 },
96
+ dkGray: { r: 169, g: 169, b: 169 }, dkGreen: { r: 0, g: 100, b: 0 },
97
+ dkKhaki: { r: 189, g: 183, b: 107 }, dkMagenta: { r: 139, g: 0, b: 139 },
98
+ dkOliveGreen: { r: 85, g: 107, b: 47 }, dkOrange: { r: 255, g: 140, b: 0 },
99
+ dkOrchid: { r: 153, g: 50, b: 204 }, dkRed: { r: 139, g: 0, b: 0 },
100
+ dkSalmon: { r: 233, g: 150, b: 122 }, dkSeaGreen: { r: 143, g: 188, b: 143 },
101
+ dkSlateBlue: { r: 72, g: 61, b: 139 }, dkSlateGray: { r: 47, g: 79, b: 79 },
102
+ dkTurquoise: { r: 0, g: 206, b: 209 }, dkViolet: { r: 148, g: 0, b: 211 },
103
+ deepPink: { r: 255, g: 20, b: 147 }, deepSkyBlue: { r: 0, g: 191, b: 255 },
104
+ dimGray: { r: 105, g: 105, b: 105 }, dodgerBlue: { r: 30, g: 144, b: 255 },
105
+ firebrick: { r: 178, g: 34, b: 34 }, floralWhite: { r: 255, g: 250, b: 240 },
106
+ forestGreen: { r: 34, g: 139, b: 34 }, fuchsia: { r: 255, g: 0, b: 255 },
107
+ gainsboro: { r: 220, g: 220, b: 220 }, ghostWhite: { r: 248, g: 248, b: 255 },
108
+ gold: { r: 255, g: 215, b: 0 }, goldenrod: { r: 218, g: 165, b: 32 },
109
+ gray: { r: 128, g: 128, b: 128 }, green: { r: 0, g: 128, b: 0 },
110
+ greenYellow: { r: 173, g: 255, b: 47 }, honeydew: { r: 240, g: 255, b: 240 },
111
+ hotPink: { r: 255, g: 105, b: 180 }, indianRed: { r: 205, g: 92, b: 92 },
112
+ indigo: { r: 75, g: 0, b: 130 }, ivory: { r: 255, g: 255, b: 240 },
113
+ khaki: { r: 240, g: 230, b: 140 }, lavender: { r: 230, g: 230, b: 250 },
114
+ lavenderBlush: { r: 255, g: 240, b: 245 }, lawnGreen: { r: 124, g: 252, b: 0 },
115
+ lemonChiffon: { r: 255, g: 250, b: 205 }, ltBlue: { r: 173, g: 216, b: 230 },
116
+ ltCoral: { r: 240, g: 128, b: 128 }, ltCyan: { r: 224, g: 255, b: 255 },
117
+ ltGoldenrodYellow: { r: 250, g: 250, b: 210 }, ltGray: { r: 211, g: 211, b: 211 },
118
+ ltGreen: { r: 144, g: 238, b: 144 }, ltPink: { r: 255, g: 182, b: 193 },
119
+ ltSalmon: { r: 255, g: 160, b: 122 }, ltSeaGreen: { r: 32, g: 178, b: 170 },
120
+ ltSkyBlue: { r: 135, g: 206, b: 250 }, ltSlateGray: { r: 119, g: 136, b: 153 },
121
+ ltSteelBlue: { r: 176, g: 196, b: 222 }, ltYellow: { r: 255, g: 255, b: 224 },
122
+ lime: { r: 0, g: 255, b: 0 }, limeGreen: { r: 50, g: 205, b: 50 },
123
+ linen: { r: 250, g: 240, b: 230 }, magenta: { r: 255, g: 0, b: 255 },
124
+ maroon: { r: 128, g: 0, b: 0 }, medAquamarine: { r: 102, g: 205, b: 170 },
125
+ medBlue: { r: 0, g: 0, b: 205 }, medOrchid: { r: 186, g: 85, b: 211 },
126
+ medPurple: { r: 147, g: 112, b: 219 }, medSeaGreen: { r: 60, g: 179, b: 113 },
127
+ medSlateBlue: { r: 123, g: 104, b: 238 }, medSpringGreen: { r: 0, g: 250, b: 154 },
128
+ medTurquoise: { r: 72, g: 209, b: 204 }, medVioletRed: { r: 199, g: 21, b: 133 },
129
+ midnightBlue: { r: 25, g: 25, b: 112 }, mintCream: { r: 245, g: 255, b: 250 },
130
+ mistyRose: { r: 255, g: 228, b: 225 }, moccasin: { r: 255, g: 228, b: 181 },
131
+ navajoWhite: { r: 255, g: 222, b: 173 }, navy: { r: 0, g: 0, b: 128 },
132
+ oldLace: { r: 253, g: 245, b: 230 }, olive: { r: 128, g: 128, b: 0 },
133
+ oliveDrab: { r: 107, g: 142, b: 35 }, orange: { r: 255, g: 165, b: 0 },
134
+ orangeRed: { r: 255, g: 69, b: 0 }, orchid: { r: 218, g: 112, b: 214 },
135
+ paleGoldenrod: { r: 238, g: 232, b: 170 }, paleGreen: { r: 152, g: 251, b: 152 },
136
+ paleTurquoise: { r: 175, g: 238, b: 238 }, paleVioletRed: { r: 219, g: 112, b: 147 },
137
+ papayaWhip: { r: 255, g: 239, b: 213 }, peachPuff: { r: 255, g: 218, b: 185 },
138
+ peru: { r: 205, g: 133, b: 63 }, pink: { r: 255, g: 192, b: 203 },
139
+ plum: { r: 221, g: 160, b: 221 }, powderBlue: { r: 176, g: 224, b: 230 },
140
+ purple: { r: 128, g: 0, b: 128 }, red: { r: 255, g: 0, b: 0 },
141
+ rosyBrown: { r: 188, g: 143, b: 143 }, royalBlue: { r: 65, g: 105, b: 225 },
142
+ saddleBrown: { r: 139, g: 69, b: 19 }, salmon: { r: 250, g: 128, b: 114 },
143
+ sandyBrown: { r: 244, g: 164, b: 96 }, seaGreen: { r: 46, g: 139, b: 87 },
144
+ seaShell: { r: 255, g: 245, b: 238 }, sienna: { r: 160, g: 82, b: 45 },
145
+ silver: { r: 192, g: 192, b: 192 }, skyBlue: { r: 135, g: 206, b: 235 },
146
+ slateBlue: { r: 106, g: 90, b: 205 }, slateGray: { r: 112, g: 128, b: 144 },
147
+ snow: { r: 255, g: 250, b: 250 }, springGreen: { r: 0, g: 255, b: 127 },
148
+ steelBlue: { r: 70, g: 130, b: 180 }, tan: { r: 210, g: 180, b: 140 },
149
+ teal: { r: 0, g: 128, b: 128 }, thistle: { r: 216, g: 191, b: 216 },
150
+ tomato: { r: 255, g: 99, b: 71 }, turquoise: { r: 64, g: 224, b: 208 },
151
+ violet: { r: 238, g: 130, b: 238 }, wheat: { r: 245, g: 222, b: 179 },
152
+ white: { r: 255, g: 255, b: 255 }, whiteSmoke: { r: 245, g: 245, b: 245 },
153
+ yellow: { r: 255, g: 255, b: 0 }, yellowGreen: { r: 154, g: 205, b: 50 },
154
+ };
155
+ function resolvePreset(val) {
156
+ return PRESET_COLORS[val] ?? { r: 0, g: 0, b: 0 };
157
+ }
158
+ // ── Transform application ───────────────────────────────────────────
159
+ function applyTransforms(rgba, transforms) {
160
+ if (!transforms)
161
+ return rgba;
162
+ let { r, g, b, a } = rgba;
163
+ for (const t of transforms) {
164
+ const v = (t.val ?? 0) / 100000;
165
+ switch (t.type) {
166
+ case "alpha":
167
+ a = v;
168
+ break;
169
+ case "alphaMod":
170
+ a *= v;
171
+ break;
172
+ case "alphaOff":
173
+ a += v;
174
+ break;
175
+ case "tint": {
176
+ r = r + (255 - r) * v;
177
+ g = g + (255 - g) * v;
178
+ b = b + (255 - b) * v;
179
+ break;
180
+ }
181
+ case "shade": {
182
+ r *= v;
183
+ g *= v;
184
+ b *= v;
185
+ break;
186
+ }
187
+ case "lumMod":
188
+ case "lumOff":
189
+ case "satMod":
190
+ case "satOff":
191
+ case "hueMod":
192
+ case "hueOff": {
193
+ const hsl = rgbToHsl(r, g, b);
194
+ if (t.type === "lumMod")
195
+ hsl.l *= v;
196
+ else if (t.type === "lumOff")
197
+ hsl.l += v;
198
+ else if (t.type === "satMod")
199
+ hsl.s *= v;
200
+ else if (t.type === "satOff")
201
+ hsl.s += v;
202
+ else if (t.type === "hueMod")
203
+ hsl.h *= v;
204
+ else
205
+ hsl.h += v * 360;
206
+ hsl.l = clamp01(hsl.l);
207
+ hsl.s = clamp01(hsl.s);
208
+ hsl.h = ((hsl.h % 360) + 360) % 360;
209
+ const c = hslToRgb(hsl.h, hsl.s, hsl.l);
210
+ r = c.r;
211
+ g = c.g;
212
+ b = c.b;
213
+ break;
214
+ }
215
+ case "comp":
216
+ case "inv":
217
+ r = 255 - r;
218
+ g = 255 - g;
219
+ b = 255 - b;
220
+ break;
221
+ case "gray": {
222
+ const avg = Math.round((r + g + b) / 3);
223
+ r = avg;
224
+ g = avg;
225
+ b = avg;
226
+ break;
227
+ }
228
+ // Channel-level transforms (rare but spec-defined)
229
+ case "red":
230
+ r = v * 255;
231
+ break;
232
+ case "redMod":
233
+ r *= v;
234
+ break;
235
+ case "redOff":
236
+ r += v * 255;
237
+ break;
238
+ case "green":
239
+ g = v * 255;
240
+ break;
241
+ case "greenMod":
242
+ g *= v;
243
+ break;
244
+ case "greenOff":
245
+ g += v * 255;
246
+ break;
247
+ case "blue":
248
+ b = v * 255;
249
+ break;
250
+ case "blueMod":
251
+ b *= v;
252
+ break;
253
+ case "blueOff":
254
+ b += v * 255;
255
+ break;
256
+ case "hue":
257
+ case "sat":
258
+ case "lum":
259
+ // Absolute set — rare; handled same as mod for safety
260
+ break;
261
+ }
262
+ }
263
+ return {
264
+ r: clamp255(Math.round(r)),
265
+ g: clamp255(Math.round(g)),
266
+ b: clamp255(Math.round(b)),
267
+ a: clamp01(a),
268
+ };
269
+ }
270
+ // ── RGB ↔ HSL helpers ───────────────────────────────────────────────
271
+ function clamp01(v) { return Math.max(0, Math.min(1, v)); }
272
+ function clamp255(v) { return Math.max(0, Math.min(255, v)); }
273
+ export function rgbToHsl(r, g, b) {
274
+ r /= 255;
275
+ g /= 255;
276
+ b /= 255;
277
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
278
+ const l = (max + min) / 2;
279
+ if (max === min)
280
+ return { h: 0, s: 0, l };
281
+ const d = max - min;
282
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
283
+ let h;
284
+ if (max === r)
285
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
286
+ else if (max === g)
287
+ h = ((b - r) / d + 2) / 6;
288
+ else
289
+ h = ((r - g) / d + 4) / 6;
290
+ return { h: h * 360, s, l };
291
+ }
292
+ export function hslToRgb(h, s, l) {
293
+ h = ((h % 360) + 360) % 360;
294
+ s = clamp01(s);
295
+ l = clamp01(l);
296
+ if (s === 0) {
297
+ const v = Math.round(l * 255);
298
+ return { r: v, g: v, b: v };
299
+ }
300
+ const hue2rgb = (p, q, t) => {
301
+ if (t < 0)
302
+ t += 1;
303
+ if (t > 1)
304
+ t -= 1;
305
+ if (t < 1 / 6)
306
+ return p + (q - p) * 6 * t;
307
+ if (t < 1 / 2)
308
+ return q;
309
+ if (t < 2 / 3)
310
+ return p + (q - p) * (2 / 3 - t) * 6;
311
+ return p;
312
+ };
313
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
314
+ const p = 2 * l - q;
315
+ const hn = h / 360;
316
+ return {
317
+ r: Math.round(hue2rgb(p, q, hn + 1 / 3) * 255),
318
+ g: Math.round(hue2rgb(p, q, hn) * 255),
319
+ b: Math.round(hue2rgb(p, q, hn - 1 / 3) * 255),
320
+ };
321
+ }
@@ -0,0 +1,2 @@
1
+ import type { FontScheme } from "../model/types.js";
2
+ export declare function resolveFontFamily(family: string | undefined, fontScheme?: FontScheme): string;
@@ -0,0 +1,66 @@
1
+ // ── macOS font substitution (mirrors TCFontUtils from OfficeImport) ─
2
+ const FONT_MAP = {
3
+ "Calibri": "Helvetica Neue",
4
+ "Calibri Light": "Helvetica Neue Light",
5
+ "Arial": "Helvetica",
6
+ "Arial Black": "Helvetica Neue",
7
+ "Arial Narrow": "Helvetica Neue",
8
+ "Cambria": "Georgia",
9
+ "Cambria Math": "Georgia",
10
+ "Consolas": "Menlo",
11
+ "Courier New": "Courier",
12
+ "Times New Roman": "Times",
13
+ "Tahoma": "Geneva",
14
+ "Verdana": "Verdana",
15
+ "Trebuchet MS": "Trebuchet MS",
16
+ "Segoe UI": "Helvetica Neue",
17
+ "Segoe UI Light": "Helvetica Neue Light",
18
+ "Segoe UI Semibold": "Helvetica Neue Medium",
19
+ "Lucida Console": "Monaco",
20
+ "Lucida Sans Unicode": "Lucida Grande",
21
+ "Palatino Linotype": "Palatino",
22
+ "Book Antiqua": "Palatino",
23
+ "Century Gothic": "Futura",
24
+ "Franklin Gothic Medium": "Avenir Next Medium",
25
+ "Garamond": "Garamond",
26
+ "Impact": "Impact",
27
+ "Corbel": "Avenir Next",
28
+ "Candara": "Avenir",
29
+ "Constantia": "Georgia",
30
+ "MS Gothic": "Hiragino Sans",
31
+ "MS Mincho": "Hiragino Mincho ProN",
32
+ "MS PGothic": "Hiragino Sans",
33
+ "MS PMincho": "Hiragino Mincho ProN",
34
+ "Yu Gothic": "Hiragino Sans",
35
+ "Yu Mincho": "Hiragino Mincho ProN",
36
+ "SimSun": "STSong",
37
+ "SimHei": "STHeiti",
38
+ "Microsoft YaHei": "PingFang SC",
39
+ "MingLiU": "LiSong Pro",
40
+ "PMingLiU": "LiSong Pro",
41
+ "Malgun Gothic": "Apple SD Gothic Neo",
42
+ "Batang": "AppleMyungjo",
43
+ };
44
+ const THEME_PREFIXES = {
45
+ "+mj-lt": { collection: "major", script: "latin" },
46
+ "+mj-ea": { collection: "major", script: "eastAsian" },
47
+ "+mj-cs": { collection: "major", script: "complexScript" },
48
+ "+mn-lt": { collection: "minor", script: "latin" },
49
+ "+mn-ea": { collection: "minor", script: "eastAsian" },
50
+ "+mn-cs": { collection: "minor", script: "complexScript" },
51
+ };
52
+ // ── Public API ──────────────────────────────────────────────────────
53
+ export function resolveFontFamily(family, fontScheme) {
54
+ if (!family) {
55
+ // Default to theme minor latin font (raw OOXML name, e.g. "Calibri")
56
+ return fontScheme?.minorFont?.latin ?? "Calibri";
57
+ }
58
+ // Resolve theme font references (+mn-lt, +mj-lt, etc.)
59
+ const ref = THEME_PREFIXES[family];
60
+ if (ref && fontScheme) {
61
+ const collection = ref.collection === "major" ? fontScheme.majorFont : fontScheme.minorFont;
62
+ family = collection[ref.script];
63
+ }
64
+ // Emit raw OOXML font name — let WebKit handle fallback
65
+ return family;
66
+ }
@@ -0,0 +1,5 @@
1
+ import type { CharacterProperties, Fill, Slide, ParagraphProperties } from "../model/types.js";
2
+ import type { PlaceholderType } from "../model/enums.js";
3
+ export declare function resolveSlideBackground(slide: Slide): Fill | undefined;
4
+ export declare function resolveCharacterProperties(local: CharacterProperties | undefined, slide: Slide, level?: number, placeholderType?: PlaceholderType): CharacterProperties;
5
+ export declare function resolveParagraphProperties(local: ParagraphProperties | undefined, slide: Slide, level?: number, placeholderType?: PlaceholderType): ParagraphProperties;
@@ -0,0 +1,106 @@
1
+ // ── Background resolution ───────────────────────────────────────────
2
+ export function resolveSlideBackground(slide) {
3
+ return slide.background?.fill
4
+ ?? slide.slideLayout.background?.fill
5
+ ?? slide.slideLayout.slideMaster.background?.fill;
6
+ }
7
+ // ── Character property inheritance ──────────────────────────────────
8
+ export function resolveCharacterProperties(local, slide, level = 0, placeholderType) {
9
+ const chain = buildPropertyChain(slide, level, placeholderType);
10
+ if (local)
11
+ chain.unshift(local);
12
+ const result = {};
13
+ for (const props of chain) {
14
+ mergeIfUndefined(result, props);
15
+ }
16
+ return result;
17
+ }
18
+ // ── Paragraph property inheritance ──────────────────────────────────
19
+ export function resolveParagraphProperties(local, slide, level = 0, placeholderType) {
20
+ const chain = buildParagraphChain(slide, level, placeholderType);
21
+ if (local)
22
+ chain.unshift(local);
23
+ const result = {};
24
+ for (const props of chain) {
25
+ mergeIfUndefined(result, props);
26
+ }
27
+ return result;
28
+ }
29
+ // ── Chain builders ──────────────────────────────────────────────────
30
+ function buildPropertyChain(slide, level, phType) {
31
+ const chain = [];
32
+ const layout = slide.slideLayout;
33
+ const master = layout.slideMaster;
34
+ // Layout placeholder default run props
35
+ if (phType) {
36
+ const layoutPh = findPlaceholder(layout.drawables, phType);
37
+ pushLevelProps(chain, layoutPh, level);
38
+ }
39
+ // Master placeholder default run props
40
+ if (phType) {
41
+ const masterPh = findPlaceholder(master.drawables, phType);
42
+ pushLevelProps(chain, masterPh, level);
43
+ }
44
+ // Master text styles by placeholder type
45
+ const masterStyle = masterTextStyleForType(master, phType);
46
+ if (masterStyle)
47
+ pushTextListLevel(chain, masterStyle, level);
48
+ return chain;
49
+ }
50
+ function buildParagraphChain(slide, level, phType) {
51
+ const chain = [];
52
+ const layout = slide.slideLayout;
53
+ const master = layout.slideMaster;
54
+ if (phType) {
55
+ const layoutPh = findPlaceholder(layout.drawables, phType);
56
+ pushLevelProps(chain, layoutPh, level);
57
+ }
58
+ if (phType) {
59
+ const masterPh = findPlaceholder(master.drawables, phType);
60
+ pushLevelProps(chain, masterPh, level);
61
+ }
62
+ const masterStyle = masterTextStyleForType(master, phType);
63
+ if (masterStyle)
64
+ pushTextListLevel(chain, masterStyle, level);
65
+ return chain;
66
+ }
67
+ // ── Helpers ─────────────────────────────────────────────────────────
68
+ function findPlaceholder(drawables, phType) {
69
+ for (const d of drawables) {
70
+ if (d.placeholder?.type === phType && "textBody" in d)
71
+ return d;
72
+ }
73
+ return undefined;
74
+ }
75
+ function pushLevelProps(chain, shape, level) {
76
+ const style = shape?.textBody?.listStyle;
77
+ if (style)
78
+ pushTextListLevel(chain, style, level);
79
+ }
80
+ function pushTextListLevel(chain, style, level) {
81
+ const props = style.levels[level];
82
+ if (props)
83
+ chain.push(props);
84
+ // Also fall through to level 0 as ultimate default
85
+ if (level > 0) {
86
+ const base = style.levels[0];
87
+ if (base)
88
+ chain.push(base);
89
+ }
90
+ }
91
+ function masterTextStyleForType(master, phType) {
92
+ if (!phType)
93
+ return master.otherTextStyle;
94
+ if (phType === "title" || phType === "ctrTitle")
95
+ return master.titleTextStyle;
96
+ if (phType === "body" || phType === "subTitle")
97
+ return master.bodyTextStyle;
98
+ return master.otherTextStyle;
99
+ }
100
+ function mergeIfUndefined(target, source) {
101
+ for (const key of Object.keys(source)) {
102
+ if (target[key] === undefined && source[key] !== undefined) {
103
+ target[key] = source[key];
104
+ }
105
+ }
106
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "quicklook-pptx-renderer",
3
+ "version": "0.1.0",
4
+ "description": "Open-source PPTX rendering engine that replicates Apple's macOS QuickLook and iOS preview — pixel for pixel. Test how PowerPoint files look on Mac/iPhone without a Mac.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "quicklook-pptx": "dist/cli.js"
10
+ },
11
+ "keywords": [
12
+ "pptx", "powerpoint", "quicklook", "macos", "ios", "officeimport",
13
+ "pptx-to-html", "pptx-to-png", "ooxml", "office-open-xml",
14
+ "presentation", "renderer", "preview", "apple", "webkit",
15
+ "python-pptx", "pptxgenjs", "document-viewer", "cross-platform",
16
+ "slides", "converter", "thumbnail", "lint", "compatibility"
17
+ ],
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ },
23
+ "./lint": {
24
+ "types": "./dist/lint.d.ts",
25
+ "import": "./dist/lint.js"
26
+ },
27
+ "./reader": {
28
+ "types": "./dist/reader/presentation.d.ts",
29
+ "import": "./dist/reader/presentation.js"
30
+ },
31
+ "./model": {
32
+ "types": "./dist/model/types.d.ts",
33
+ "import": "./dist/model/types.js"
34
+ },
35
+ "./mapper": {
36
+ "types": "./dist/mapper/html-generator.d.ts",
37
+ "import": "./dist/mapper/html-generator.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "!dist/tools",
43
+ "!dist/types"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsc",
47
+ "dev": "tsc --watch",
48
+ "lint:pptx": "node dist/cli.js lint"
49
+ },
50
+ "engines": {
51
+ "node": ">=20"
52
+ },
53
+ "license": "MIT",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/Fornace/quicklook-pptx-renderer.git"
57
+ },
58
+ "dependencies": {
59
+ "fast-xml-parser": "^5.2.0",
60
+ "jszip": "^3.10.1"
61
+ },
62
+ "optionalDependencies": {
63
+ "@napi-rs/canvas": "^0.1.65"
64
+ },
65
+ "devDependencies": {
66
+ "@types/node": "^22.0.0",
67
+ "@types/pngjs": "^6.0.5",
68
+ "pixelmatch": "^6.0.0",
69
+ "playwright": "^1.59.1",
70
+ "pngjs": "^7.0.0",
71
+ "pptxgenjs": "^4.0.1",
72
+ "typescript": "^5.7.0"
73
+ }
74
+ }