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,127 @@
1
+ import { readTheme } from "./theme.js";
2
+ import { readTextListStyle } from "./text-list-style.js";
3
+ export { readTextListStyle };
4
+ import { readSlideMaster } from "./slide-master.js";
5
+ import { readSlideLayout } from "./slide-layout.js";
6
+ import { readSlide } from "./slide.js";
7
+ function toArray(val) {
8
+ return val == null ? [] : Array.isArray(val) ? val : [val];
9
+ }
10
+ function readSize(node) {
11
+ return {
12
+ cx: Number(node?.["@_cx"] ?? 0),
13
+ cy: Number(node?.["@_cy"] ?? 0),
14
+ };
15
+ }
16
+ function relsByType(rels, type) {
17
+ return [...rels.values()].filter((r) => r.type === type);
18
+ }
19
+ const EMPTY_THEME = {
20
+ name: "",
21
+ colorScheme: { name: "", colors: {} },
22
+ fontScheme: {
23
+ name: "",
24
+ majorFont: { latin: "", eastAsian: "", complexScript: "", scriptFonts: {} },
25
+ minorFont: { latin: "", eastAsian: "", complexScript: "", scriptFonts: {} },
26
+ },
27
+ styleMatrix: { fillStyles: [], lineStyles: [], effectStyles: [], bgFillStyles: [] },
28
+ };
29
+ export async function readPresentation(pkg) {
30
+ const presXml = await pkg.getPartXml("ppt/presentation.xml");
31
+ const presNode = presXml?.Presentation ?? presXml?.presentation ?? presXml ?? {};
32
+ const slideSize = readSize(presNode.sldSz);
33
+ const notesSize = readSize(presNode.notesSz);
34
+ const defaultTextStyle = presNode.defaultTextStyle
35
+ ? readTextListStyle(presNode.defaultTextStyle)
36
+ : undefined;
37
+ const presRels = await pkg.getRelationships("ppt/presentation.xml");
38
+ const layoutsByPath = new Map();
39
+ // --- Slide masters ---
40
+ const masterIds = toArray(presNode.sldMasterIdLst?.sldMasterId);
41
+ const masters = [];
42
+ for (const mid of masterIds) {
43
+ // r:id attribute becomes @_id after removeNSPrefix (collides with numeric id, but rId value wins)
44
+ const rId = mid["@_rId"] ?? mid["@_id"];
45
+ const rel = presRels.get(rId);
46
+ if (!rel)
47
+ continue;
48
+ const masterPath = pkg.resolveRelTarget("ppt/presentation.xml", rel.target);
49
+ const masterXml = await pkg.getPartXml(masterPath);
50
+ if (!masterXml)
51
+ continue;
52
+ const masterPartial = readSlideMaster(masterXml);
53
+ const masterRels = await pkg.getRelationships(masterPath);
54
+ // Read theme
55
+ const themeRel = relsByType(masterRels, "theme")[0];
56
+ let theme = EMPTY_THEME;
57
+ if (themeRel) {
58
+ const themePath = pkg.resolveRelTarget(masterPath, themeRel.target);
59
+ const themeXml = await pkg.getPartXml(themePath);
60
+ if (themeXml)
61
+ theme = readTheme(themeXml);
62
+ }
63
+ // Read slide layouts for this master
64
+ const layoutRels = relsByType(masterRels, "slideLayout");
65
+ const layouts = [];
66
+ for (const lr of layoutRels) {
67
+ const layoutPath = pkg.resolveRelTarget(masterPath, lr.target);
68
+ const layoutXml = await pkg.getPartXml(layoutPath);
69
+ if (!layoutXml)
70
+ continue;
71
+ const layoutPartial = readSlideLayout(layoutXml);
72
+ const layout = { ...layoutPartial, slideMaster: null };
73
+ layouts.push(layout);
74
+ layoutsByPath.set(layoutPath, layout);
75
+ }
76
+ const master = {
77
+ ...masterPartial,
78
+ theme,
79
+ slideLayouts: layouts,
80
+ };
81
+ // Wire layout->master back-references
82
+ for (const layout of layouts) {
83
+ layout.slideMaster = master;
84
+ }
85
+ masters.push(master);
86
+ }
87
+ // --- Slides ---
88
+ const slideIds = toArray(presNode.sldIdLst?.sldId);
89
+ const slides = [];
90
+ for (const sid of slideIds) {
91
+ const rId = sid["@_rId"] ?? sid["@_id"];
92
+ const rel = presRels.get(rId);
93
+ if (!rel)
94
+ continue;
95
+ const slidePath = pkg.resolveRelTarget("ppt/presentation.xml", rel.target);
96
+ const slideXml = await pkg.getPartXml(slidePath);
97
+ if (!slideXml)
98
+ continue;
99
+ // Pass raw XML so readDrawables can recover document order
100
+ const rawBuf = await pkg.getPartBuffer(slidePath);
101
+ const rawXml = rawBuf?.toString("utf8");
102
+ const slidePartial = readSlide(slideXml, rawXml);
103
+ const slideRels = await pkg.getRelationships(slidePath);
104
+ // Find this slide's layout
105
+ const layoutRel = relsByType(slideRels, "slideLayout")[0];
106
+ let slideLayout;
107
+ if (layoutRel) {
108
+ const layoutPath = pkg.resolveRelTarget(slidePath, layoutRel.target);
109
+ slideLayout = layoutsByPath.get(layoutPath);
110
+ }
111
+ // Fallback: first layout of first master
112
+ if (!slideLayout && masters.length > 0 && masters[0].slideLayouts.length > 0) {
113
+ slideLayout = masters[0].slideLayouts[0];
114
+ }
115
+ slides.push({
116
+ ...slidePartial,
117
+ slideLayout: slideLayout,
118
+ });
119
+ }
120
+ return {
121
+ slides,
122
+ slideMasters: masters,
123
+ slideSize,
124
+ notesSize,
125
+ defaultTextStyle,
126
+ };
127
+ }
@@ -0,0 +1,2 @@
1
+ import type { SlideLayout } from "../model/types.js";
2
+ export declare function readSlideLayout(xml: any): Omit<SlideLayout, "slideMaster">;
@@ -0,0 +1,28 @@
1
+ import { readDrawables } from "./drawing.js";
2
+ import { readBackground, readColorMap } from "./slide-master.js";
3
+ function readColorMapOverride(node) {
4
+ if (node == null)
5
+ return undefined;
6
+ if (node.overrideClrMapping)
7
+ return readColorMap(node.overrideClrMapping);
8
+ // masterClrMapping means "use master's map" — no override
9
+ return undefined;
10
+ }
11
+ export function readSlideLayout(xml) {
12
+ const root = xml.sldLayout ?? xml;
13
+ const cSld = root.cSld ?? {};
14
+ const spTree = cSld.spTree ?? {};
15
+ const result = {
16
+ drawables: readDrawables(spTree),
17
+ };
18
+ const bg = readBackground(cSld.bg);
19
+ if (bg)
20
+ result.background = bg;
21
+ const layoutType = root["@_type"];
22
+ if (layoutType)
23
+ result.layoutType = layoutType;
24
+ const colorMapOverride = readColorMapOverride(root.clrMapOvr);
25
+ if (colorMapOverride)
26
+ result.colorMapOverride = colorMapOverride;
27
+ return result;
28
+ }
@@ -0,0 +1,4 @@
1
+ import type { SlideMaster, ColorMap, Background } from "../model/types.js";
2
+ export declare function readColorMap(node: any): ColorMap;
3
+ export declare function readBackground(node: any): Background | undefined;
4
+ export declare function readSlideMaster(xml: any): Omit<SlideMaster, "theme" | "slideLayouts">;
@@ -0,0 +1,49 @@
1
+ import { readDrawables } from "./drawing.js";
2
+ import { readFill } from "./fill.js";
3
+ import { readTextListStyle } from "./text-list-style.js";
4
+ const COLOR_MAP_KEYS = [
5
+ "bg1", "tx1", "bg2", "tx2",
6
+ "accent1", "accent2", "accent3", "accent4", "accent5", "accent6",
7
+ "hlink", "folHlink",
8
+ ];
9
+ export function readColorMap(node) {
10
+ const mappings = {};
11
+ for (const key of COLOR_MAP_KEYS) {
12
+ const val = node[`@_${key}`];
13
+ if (val)
14
+ mappings[key] = val;
15
+ }
16
+ return { mappings };
17
+ }
18
+ export function readBackground(node) {
19
+ if (node == null)
20
+ return undefined;
21
+ const bgPr = node.bgPr;
22
+ if (bgPr) {
23
+ const fill = readFill(bgPr);
24
+ return fill ? { fill } : undefined;
25
+ }
26
+ return undefined;
27
+ }
28
+ export function readSlideMaster(xml) {
29
+ const root = xml.sldMaster ?? xml;
30
+ const cSld = root.cSld ?? {};
31
+ const spTree = cSld.spTree ?? {};
32
+ const result = {
33
+ drawables: readDrawables(spTree),
34
+ colorMap: readColorMap(root.clrMap ?? {}),
35
+ };
36
+ const bg = readBackground(cSld.bg);
37
+ if (bg)
38
+ result.background = bg;
39
+ const txStyles = root.txStyles;
40
+ if (txStyles) {
41
+ if (txStyles.titleStyle)
42
+ result.titleTextStyle = readTextListStyle(txStyles.titleStyle);
43
+ if (txStyles.bodyStyle)
44
+ result.bodyTextStyle = readTextListStyle(txStyles.bodyStyle);
45
+ if (txStyles.otherStyle)
46
+ result.otherTextStyle = readTextListStyle(txStyles.otherStyle);
47
+ }
48
+ return result;
49
+ }
@@ -0,0 +1,2 @@
1
+ import type { Slide } from "../model/types.js";
2
+ export declare function readSlide(xml: any, rawXml?: string): Omit<Slide, "slideLayout">;
@@ -0,0 +1,26 @@
1
+ import { readDrawables } from "./drawing.js";
2
+ import { readBackground, readColorMap } from "./slide-master.js";
3
+ function readColorMapOverride(node) {
4
+ if (node == null)
5
+ return undefined;
6
+ if (node.overrideClrMapping)
7
+ return readColorMap(node.overrideClrMapping);
8
+ return undefined;
9
+ }
10
+ export function readSlide(xml, rawXml) {
11
+ const root = xml.sld ?? xml;
12
+ const cSld = root.cSld ?? {};
13
+ const spTree = cSld.spTree ?? {};
14
+ const result = {
15
+ drawables: readDrawables(spTree, rawXml),
16
+ };
17
+ if (cSld["@_name"])
18
+ result.name = cSld["@_name"];
19
+ const bg = readBackground(cSld.bg);
20
+ if (bg)
21
+ result.background = bg;
22
+ const colorMapOverride = readColorMapOverride(root.clrMapOvr);
23
+ if (colorMapOverride)
24
+ result.colorMapOverride = colorMapOverride;
25
+ return result;
26
+ }
@@ -0,0 +1,2 @@
1
+ import type { TextListStyle } from "../model/types.js";
2
+ export declare function readTextListStyle(node: any): TextListStyle;
@@ -0,0 +1,9 @@
1
+ import { readParagraphProperties } from "./text.js";
2
+ export function readTextListStyle(node) {
3
+ const levels = [];
4
+ for (let i = 1; i <= 9; i++) {
5
+ const lvl = node[`lvl${i}pPr`];
6
+ levels.push(lvl ? readParagraphProperties(lvl) : undefined);
7
+ }
8
+ return { levels };
9
+ }
@@ -0,0 +1,5 @@
1
+ import type { TextBody, Paragraph, CharacterProperties, ParagraphProperties } from "../model/types.js";
2
+ export declare function readCharacterProperties(rPr: any): CharacterProperties;
3
+ export declare function readParagraphProperties(pPr: any): ParagraphProperties;
4
+ export declare function readParagraph(pNode: any): Paragraph;
5
+ export declare function readTextBody(node: any): TextBody;
@@ -0,0 +1,295 @@
1
+ import { readColor } from "./color.js";
2
+ import { readFill } from "./fill.js";
3
+ import { readEffects } from "./effects.js";
4
+ function toArray(val) {
5
+ return val == null ? [] : Array.isArray(val) ? val : [val];
6
+ }
7
+ function toNum(val) {
8
+ return val == null ? undefined : Number(val);
9
+ }
10
+ function toBool(val) {
11
+ return val === "1" || val === "true";
12
+ }
13
+ function readSpacing(node) {
14
+ if (node == null)
15
+ return undefined;
16
+ if (node.spcPct)
17
+ return { type: "pct", val: Number(node.spcPct["@_val"] ?? 0) };
18
+ if (node.spcPts)
19
+ return { type: "pts", val: Number(node.spcPts["@_val"] ?? 0) };
20
+ return undefined;
21
+ }
22
+ function readLineEnd(node) {
23
+ if (node == null)
24
+ return undefined;
25
+ const type = node["@_type"];
26
+ if (!type)
27
+ return undefined;
28
+ const end = { type };
29
+ if (node["@_w"])
30
+ end.width = node["@_w"];
31
+ if (node["@_len"])
32
+ end.length = node["@_len"];
33
+ return end;
34
+ }
35
+ function readStroke(node) {
36
+ if (node == null)
37
+ return undefined;
38
+ const s = {};
39
+ if (node["@_w"] != null)
40
+ s.width = Number(node["@_w"]);
41
+ if (node["@_cap"])
42
+ s.cap = node["@_cap"];
43
+ if (node["@_cmpd"])
44
+ s.compound = node["@_cmpd"];
45
+ const fill = readFill(node);
46
+ if (fill)
47
+ s.fill = fill;
48
+ if (node.prstDash?.["@_val"])
49
+ s.dash = node.prstDash["@_val"];
50
+ if (node.round != null)
51
+ s.join = "round";
52
+ else if (node.bevel != null)
53
+ s.join = "bevel";
54
+ else if (node.miter != null) {
55
+ s.join = "miter";
56
+ if (node.miter["@_lim"] != null)
57
+ s.miterLimit = Number(node.miter["@_lim"]);
58
+ }
59
+ const head = readLineEnd(node.headEnd);
60
+ const tail = readLineEnd(node.tailEnd);
61
+ if (head)
62
+ s.headEnd = head;
63
+ if (tail)
64
+ s.tailEnd = tail;
65
+ return s;
66
+ }
67
+ export function readCharacterProperties(rPr) {
68
+ const cp = {};
69
+ if (rPr == null)
70
+ return cp;
71
+ if (rPr.latin?.["@_typeface"])
72
+ cp.latinFont = rPr.latin["@_typeface"];
73
+ if (rPr.ea?.["@_typeface"])
74
+ cp.eastAsianFont = rPr.ea["@_typeface"];
75
+ if (rPr.cs?.["@_typeface"])
76
+ cp.complexScriptFont = rPr.cs["@_typeface"];
77
+ if (rPr.sym?.["@_typeface"])
78
+ cp.symbolFont = rPr.sym["@_typeface"];
79
+ cp.fontSize = toNum(rPr["@_sz"]);
80
+ if (rPr["@_b"] != null)
81
+ cp.bold = toBool(rPr["@_b"]);
82
+ if (rPr["@_i"] != null)
83
+ cp.italic = toBool(rPr["@_i"]);
84
+ if (rPr["@_u"] != null)
85
+ cp.underline = rPr["@_u"];
86
+ if (rPr["@_strike"] != null)
87
+ cp.strikethrough = rPr["@_strike"];
88
+ if (rPr["@_cap"] != null)
89
+ cp.caps = rPr["@_cap"];
90
+ cp.baseline = toNum(rPr["@_baseline"]);
91
+ cp.spacing = toNum(rPr["@_spc"]);
92
+ cp.kerning = toNum(rPr["@_kern"]);
93
+ if (rPr["@_lang"])
94
+ cp.lang = rPr["@_lang"];
95
+ if (rPr["@_dirty"] != null)
96
+ cp.dirty = toBool(rPr["@_dirty"]);
97
+ const fill = readFill(rPr);
98
+ if (fill)
99
+ cp.fill = fill;
100
+ const highlight = readColor(rPr.highlight ?? rPr.highlightClr);
101
+ if (highlight)
102
+ cp.highlight = highlight;
103
+ const stroke = readStroke(rPr.ln);
104
+ if (stroke)
105
+ cp.stroke = stroke;
106
+ const uFill = readFill(rPr.uFill);
107
+ if (uFill)
108
+ cp.underlineFill = uFill;
109
+ const effects = readEffects(rPr.effectLst);
110
+ if (effects.length)
111
+ cp.effects = effects;
112
+ if (rPr.hlinkClick?.["@_id"])
113
+ cp.hyperlink = rPr.hlinkClick["@_id"];
114
+ return cp;
115
+ }
116
+ function readBullet(pPr) {
117
+ if (pPr.buNone != null)
118
+ return { type: "none" };
119
+ if (pPr.buChar != null) {
120
+ // Decode XML character references (&#xHHHH;) that fast-xml-parser leaves as literal strings
121
+ const raw = pPr.buChar["@_char"] ?? "";
122
+ const decoded = raw.replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCodePoint(parseInt(hex, 16)))
123
+ .replace(/&#(\d+);/g, (_, dec) => String.fromCodePoint(parseInt(dec, 10)));
124
+ return { type: "char", char: decoded };
125
+ }
126
+ if (pPr.buAutoNum != null) {
127
+ const b = {
128
+ type: "autoNum",
129
+ autoNumScheme: pPr.buAutoNum["@_type"],
130
+ };
131
+ if (pPr.buAutoNum["@_startAt"] != null)
132
+ b.startAt = Number(pPr.buAutoNum["@_startAt"]);
133
+ return b;
134
+ }
135
+ if (pPr.buBlip != null) {
136
+ return { type: "blip", blipRId: pPr.buBlip.blip?.["@_embed"] };
137
+ }
138
+ return undefined;
139
+ }
140
+ export function readParagraphProperties(pPr) {
141
+ const pp = readCharacterProperties(pPr?.defRPr);
142
+ if (pPr == null)
143
+ return pp;
144
+ pp.level = toNum(pPr["@_lvl"]);
145
+ if (pPr["@_algn"])
146
+ pp.align = pPr["@_algn"];
147
+ pp.marL = toNum(pPr["@_marL"]);
148
+ pp.marR = toNum(pPr["@_marR"]);
149
+ pp.indent = toNum(pPr["@_indent"]);
150
+ pp.defTabSz = toNum(pPr["@_defTabSz"]);
151
+ if (pPr["@_rtl"] != null)
152
+ pp.rtl = toBool(pPr["@_rtl"]);
153
+ const lnSpc = readSpacing(pPr.lnSpc);
154
+ if (lnSpc)
155
+ pp.lineSpacing = lnSpc;
156
+ const spcBef = readSpacing(pPr.spcBef);
157
+ if (spcBef)
158
+ pp.spaceBefore = spcBef;
159
+ const spcAft = readSpacing(pPr.spcAft);
160
+ if (spcAft)
161
+ pp.spaceAfter = spcAft;
162
+ const bullet = readBullet(pPr);
163
+ if (bullet)
164
+ pp.bullet = bullet;
165
+ const bulletClr = readColor(pPr.buClr);
166
+ if (bulletClr)
167
+ pp.bulletColor = bulletClr;
168
+ if (pPr.buFont?.["@_typeface"])
169
+ pp.bulletFont = pPr.buFont["@_typeface"];
170
+ if (pPr.buSzPct?.["@_val"] != null)
171
+ pp.bulletSizePercent = Number(pPr.buSzPct["@_val"]);
172
+ if (pPr.buSzPts?.["@_val"] != null)
173
+ pp.bulletSizePoints = Number(pPr.buSzPts["@_val"]);
174
+ return pp;
175
+ }
176
+ function readRun(rNode) {
177
+ const run = {
178
+ type: "r",
179
+ text: rNode.t ?? "",
180
+ };
181
+ if (rNode.rPr) {
182
+ const props = readCharacterProperties(rNode.rPr);
183
+ if (Object.keys(props).length)
184
+ run.properties = props;
185
+ }
186
+ return run;
187
+ }
188
+ function readBreak(brNode) {
189
+ const br = { type: "br" };
190
+ if (brNode.rPr) {
191
+ const props = readCharacterProperties(brNode.rPr);
192
+ if (Object.keys(props).length)
193
+ br.properties = props;
194
+ }
195
+ return br;
196
+ }
197
+ function readField(fldNode) {
198
+ const fld = {
199
+ type: "fld",
200
+ text: fldNode.t ?? "",
201
+ };
202
+ if (fldNode["@_type"])
203
+ fld.fieldType = fldNode["@_type"];
204
+ if (fldNode.rPr) {
205
+ const props = readCharacterProperties(fldNode.rPr);
206
+ if (Object.keys(props).length)
207
+ fld.properties = props;
208
+ }
209
+ return fld;
210
+ }
211
+ export function readParagraph(pNode) {
212
+ const runs = [];
213
+ for (const r of toArray(pNode.r))
214
+ runs.push(readRun(r));
215
+ for (const br of toArray(pNode.br))
216
+ runs.push(readBreak(br));
217
+ for (const fld of toArray(pNode.fld))
218
+ runs.push(readField(fld));
219
+ const para = { runs };
220
+ if (pNode.pPr) {
221
+ const props = readParagraphProperties(pNode.pPr);
222
+ if (Object.keys(props).length)
223
+ para.properties = props;
224
+ }
225
+ if (pNode.endParaRPr) {
226
+ const ep = readCharacterProperties(pNode.endParaRPr);
227
+ if (Object.keys(ep).length)
228
+ para.endParaRPr = ep;
229
+ }
230
+ return para;
231
+ }
232
+ function readBodyProperties(bp) {
233
+ const props = {};
234
+ if (bp == null)
235
+ return props;
236
+ if (bp["@_wrap"])
237
+ props.wrap = bp["@_wrap"];
238
+ props.lIns = toNum(bp["@_lIns"]);
239
+ props.tIns = toNum(bp["@_tIns"]);
240
+ props.rIns = toNum(bp["@_rIns"]);
241
+ props.bIns = toNum(bp["@_bIns"]);
242
+ if (bp["@_anchor"])
243
+ props.anchor = bp["@_anchor"];
244
+ if (bp["@_anchorCtr"] != null)
245
+ props.anchorCtr = toBool(bp["@_anchorCtr"]);
246
+ if (bp["@_rtlCol"] != null)
247
+ props.rtlCol = toBool(bp["@_rtlCol"]);
248
+ if (bp["@_vert"])
249
+ props.vert = bp["@_vert"];
250
+ props.rot = toNum(bp["@_rot"]);
251
+ props.numCol = toNum(bp["@_numCol"]);
252
+ props.spcCol = toNum(bp["@_spcCol"]);
253
+ if (bp.noAutofit != null)
254
+ props.autoFit = "noAutoFit";
255
+ else if (bp.spAutoFit != null)
256
+ props.autoFit = "spAutoFit";
257
+ else if (bp.normAutofit != null) {
258
+ props.autoFit = "normAutoFit";
259
+ props.fontScale = toNum(bp.normAutofit["@_fontScale"]);
260
+ props.lnSpcReduction = toNum(bp.normAutofit["@_lnSpcReduction"]);
261
+ }
262
+ return props;
263
+ }
264
+ const LEVEL_TAGS = ["lvl1pPr", "lvl2pPr", "lvl3pPr", "lvl4pPr", "lvl5pPr", "lvl6pPr", "lvl7pPr", "lvl8pPr", "lvl9pPr"];
265
+ function readListStyle(node) {
266
+ if (node == null)
267
+ return undefined;
268
+ const levels = [];
269
+ let any = false;
270
+ for (let i = 0; i < LEVEL_TAGS.length; i++) {
271
+ const lvl = node[LEVEL_TAGS[i]];
272
+ if (lvl) {
273
+ levels[i] = readParagraphProperties(lvl);
274
+ any = true;
275
+ }
276
+ }
277
+ // Also handle defPPr as level 0 fallback
278
+ if (node.defPPr) {
279
+ levels[0] = readParagraphProperties(node.defPPr);
280
+ any = true;
281
+ }
282
+ return any ? { levels } : undefined;
283
+ }
284
+ export function readTextBody(node) {
285
+ const body = {
286
+ paragraphs: toArray(node.p).map(readParagraph),
287
+ };
288
+ const bp = readBodyProperties(node.bodyPr);
289
+ if (Object.keys(bp).length)
290
+ body.properties = bp;
291
+ const ls = readListStyle(node.lstStyle);
292
+ if (ls)
293
+ body.listStyle = ls;
294
+ return body;
295
+ }
@@ -0,0 +1,2 @@
1
+ import type { Theme } from "../model/types.js";
2
+ export declare function readTheme(xml: any): Theme;
@@ -0,0 +1,109 @@
1
+ import { readColor } from "./color.js";
2
+ import { readFill } from "./fill.js";
3
+ import { readEffects } from "./effects.js";
4
+ function toArray(val) {
5
+ return val == null ? [] : Array.isArray(val) ? val : [val];
6
+ }
7
+ const SCHEME_COLOR_NAMES = [
8
+ "dk1", "lt1", "dk2", "lt2",
9
+ "accent1", "accent2", "accent3", "accent4", "accent5", "accent6",
10
+ "hlink", "folHlink",
11
+ ];
12
+ function readColorScheme(node) {
13
+ const colors = {};
14
+ for (const name of SCHEME_COLOR_NAMES) {
15
+ const el = node[name];
16
+ if (el == null)
17
+ continue;
18
+ const c = readColor(el);
19
+ if (c)
20
+ colors[name] = c;
21
+ }
22
+ return { name: node["@_name"] ?? "", colors };
23
+ }
24
+ function readFontCollection(node) {
25
+ const scriptFonts = {};
26
+ for (const f of toArray(node.font)) {
27
+ if (f["@_script"] && f["@_typeface"]) {
28
+ scriptFonts[f["@_script"]] = f["@_typeface"];
29
+ }
30
+ }
31
+ return {
32
+ latin: node.latin?.["@_typeface"] ?? "",
33
+ eastAsian: node.ea?.["@_typeface"] ?? "",
34
+ complexScript: node.cs?.["@_typeface"] ?? "",
35
+ scriptFonts,
36
+ };
37
+ }
38
+ function readFontScheme(node) {
39
+ return {
40
+ name: node["@_name"] ?? "",
41
+ majorFont: readFontCollection(node.majorFont ?? {}),
42
+ minorFont: readFontCollection(node.minorFont ?? {}),
43
+ };
44
+ }
45
+ function readStroke(node) {
46
+ const s = {};
47
+ if (node["@_w"] != null)
48
+ s.width = Number(node["@_w"]);
49
+ if (node["@_cap"])
50
+ s.cap = node["@_cap"];
51
+ if (node["@_cmpd"])
52
+ s.compound = node["@_cmpd"];
53
+ const fill = readFill(node);
54
+ if (fill)
55
+ s.fill = fill;
56
+ if (node.prstDash?.["@_val"])
57
+ s.dash = node.prstDash["@_val"];
58
+ if (node.round != null)
59
+ s.join = "round";
60
+ else if (node.bevel != null)
61
+ s.join = "bevel";
62
+ else if (node.miter != null) {
63
+ s.join = "miter";
64
+ if (node.miter["@_lim"] != null)
65
+ s.miterLimit = Number(node.miter["@_lim"]);
66
+ }
67
+ return s;
68
+ }
69
+ function readStyleMatrix(node) {
70
+ const fillStyles = toArray(node.fillStyleLst?.solidFill)
71
+ .map((n) => readFill({ solidFill: n }))
72
+ .filter(Boolean);
73
+ // fillStyleLst can contain mixed fill types; read all children in order
74
+ const fillStyleLst = node.fillStyleLst ?? {};
75
+ const allFills = [];
76
+ for (const key of ["solidFill", "gradFill", "blipFill", "pattFill", "noFill", "grpFill"]) {
77
+ for (const el of toArray(fillStyleLst[key])) {
78
+ const f = readFill({ [key]: el });
79
+ if (f)
80
+ allFills.push(f);
81
+ }
82
+ }
83
+ const bgFillStyleLst = node.bgFillStyleLst ?? {};
84
+ const bgFills = [];
85
+ for (const key of ["solidFill", "gradFill", "blipFill", "pattFill", "noFill", "grpFill"]) {
86
+ for (const el of toArray(bgFillStyleLst[key])) {
87
+ const f = readFill({ [key]: el });
88
+ if (f)
89
+ bgFills.push(f);
90
+ }
91
+ }
92
+ const lineStyles = toArray(node.lnStyleLst?.ln).map(readStroke);
93
+ const effectStyles = toArray(node.effectStyleLst?.effectStyle).map((es) => readEffects(es.effectLst));
94
+ return {
95
+ fillStyles: allFills,
96
+ lineStyles: lineStyles,
97
+ effectStyles,
98
+ bgFillStyles: bgFills,
99
+ };
100
+ }
101
+ export function readTheme(xml) {
102
+ const te = xml.theme?.themeElements ?? xml.themeElements ?? {};
103
+ return {
104
+ name: xml.theme?.["@_name"] ?? xml["@_name"] ?? "",
105
+ colorScheme: readColorScheme(te.clrScheme ?? {}),
106
+ fontScheme: readFontScheme(te.fontScheme ?? {}),
107
+ styleMatrix: readStyleMatrix(te.fmtScheme ?? {}),
108
+ };
109
+ }
@@ -0,0 +1,2 @@
1
+ import type { OrientedBounds } from "../model/types.js";
2
+ export declare function readTransform(xfrm: any): OrientedBounds | undefined;