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.
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +175 -0
- package/dist/diff/compare.d.ts +17 -0
- package/dist/diff/compare.js +71 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +72 -0
- package/dist/lint.d.ts +27 -0
- package/dist/lint.js +328 -0
- package/dist/mapper/bleed-map.d.ts +6 -0
- package/dist/mapper/bleed-map.js +1 -0
- package/dist/mapper/constants.d.ts +2 -0
- package/dist/mapper/constants.js +4 -0
- package/dist/mapper/drawable-mapper.d.ts +16 -0
- package/dist/mapper/drawable-mapper.js +1464 -0
- package/dist/mapper/html-generator.d.ts +13 -0
- package/dist/mapper/html-generator.js +539 -0
- package/dist/mapper/image-mapper.d.ts +14 -0
- package/dist/mapper/image-mapper.js +70 -0
- package/dist/mapper/nano-malloc.d.ts +130 -0
- package/dist/mapper/nano-malloc.js +197 -0
- package/dist/mapper/ql-bleed.d.ts +35 -0
- package/dist/mapper/ql-bleed.js +254 -0
- package/dist/mapper/shape-mapper.d.ts +41 -0
- package/dist/mapper/shape-mapper.js +2384 -0
- package/dist/mapper/slide-mapper.d.ts +4 -0
- package/dist/mapper/slide-mapper.js +112 -0
- package/dist/mapper/style-builder.d.ts +12 -0
- package/dist/mapper/style-builder.js +30 -0
- package/dist/mapper/text-mapper.d.ts +14 -0
- package/dist/mapper/text-mapper.js +302 -0
- package/dist/model/enums.d.ts +25 -0
- package/dist/model/enums.js +2 -0
- package/dist/model/types.d.ts +482 -0
- package/dist/model/types.js +7 -0
- package/dist/package/content-types.d.ts +1 -0
- package/dist/package/content-types.js +4 -0
- package/dist/package/package.d.ts +10 -0
- package/dist/package/package.js +52 -0
- package/dist/package/relationships.d.ts +6 -0
- package/dist/package/relationships.js +25 -0
- package/dist/package/zip.d.ts +6 -0
- package/dist/package/zip.js +17 -0
- package/dist/reader/color.d.ts +3 -0
- package/dist/reader/color.js +79 -0
- package/dist/reader/drawing.d.ts +17 -0
- package/dist/reader/drawing.js +403 -0
- package/dist/reader/effects.d.ts +2 -0
- package/dist/reader/effects.js +83 -0
- package/dist/reader/fill.d.ts +2 -0
- package/dist/reader/fill.js +94 -0
- package/dist/reader/presentation.d.ts +5 -0
- package/dist/reader/presentation.js +127 -0
- package/dist/reader/slide-layout.d.ts +2 -0
- package/dist/reader/slide-layout.js +28 -0
- package/dist/reader/slide-master.d.ts +4 -0
- package/dist/reader/slide-master.js +49 -0
- package/dist/reader/slide.d.ts +2 -0
- package/dist/reader/slide.js +26 -0
- package/dist/reader/text-list-style.d.ts +2 -0
- package/dist/reader/text-list-style.js +9 -0
- package/dist/reader/text.d.ts +5 -0
- package/dist/reader/text.js +295 -0
- package/dist/reader/theme.d.ts +2 -0
- package/dist/reader/theme.js +109 -0
- package/dist/reader/transform.d.ts +2 -0
- package/dist/reader/transform.js +21 -0
- package/dist/render/image-renderer.d.ts +3 -0
- package/dist/render/image-renderer.js +33 -0
- package/dist/render/renderer.d.ts +9 -0
- package/dist/render/renderer.js +178 -0
- package/dist/render/shape-renderer.d.ts +3 -0
- package/dist/render/shape-renderer.js +175 -0
- package/dist/render/text-renderer.d.ts +3 -0
- package/dist/render/text-renderer.js +152 -0
- package/dist/resolve/color-resolver.d.ts +18 -0
- package/dist/resolve/color-resolver.js +321 -0
- package/dist/resolve/font-map.d.ts +2 -0
- package/dist/resolve/font-map.js +66 -0
- package/dist/resolve/inheritance.d.ts +5 -0
- package/dist/resolve/inheritance.js +106 -0
- 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,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
|
+
}
|