@veryfront/ext-document-kreuzberg 0.1.994 → 0.1.996
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.
|
@@ -23,6 +23,12 @@ function decodeXmlText(value) {
|
|
|
23
23
|
function extractPptxSlideText(xml) {
|
|
24
24
|
return Array.from(xml.matchAll(/<a:t(?:\s[^>]*)?>([\s\S]*?)<\/a:t>/g)).map((match) => decodeXmlText(match[1] ?? "").trim()).filter(Boolean).join(" ").trim();
|
|
25
25
|
}
|
|
26
|
+
function normalizePptxText(value) {
|
|
27
|
+
return value.replace(/\s+/g, " ").trim();
|
|
28
|
+
}
|
|
29
|
+
function cleanExtractedMarkdown(value) {
|
|
30
|
+
return value.replace(/!\[[^\]\n]*\]\(\s*\)/g, "").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
31
|
+
}
|
|
26
32
|
function slideNumber(path) {
|
|
27
33
|
return Number(path.match(/\/slide(\d+)\.xml$/)?.[1] ?? 0);
|
|
28
34
|
}
|
|
@@ -30,6 +36,72 @@ function getXmlAttribute(tag, name) {
|
|
|
30
36
|
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31
37
|
return tag.match(new RegExp(`\\s${escapedName}=(["'])(.*?)\\1`))?.[2];
|
|
32
38
|
}
|
|
39
|
+
function pptxShapeRole(shapeXml) {
|
|
40
|
+
const placeholderTag = shapeXml.match(/<(?:\w+:)?ph\b[^>]*>/)?.[0] ?? "";
|
|
41
|
+
const placeholderType = getXmlAttribute(placeholderTag, "type");
|
|
42
|
+
if (placeholderType === "title" || placeholderType === "ctrTitle") return "title";
|
|
43
|
+
if (placeholderType === "subTitle") return "subtitle";
|
|
44
|
+
const propertyTag = shapeXml.match(/<(?:\w+:)?cNvPr\b[^>]*>/)?.[0] ?? "";
|
|
45
|
+
const name = getXmlAttribute(propertyTag, "name")?.toLowerCase() ?? "";
|
|
46
|
+
if (name.includes("subtitle")) return "subtitle";
|
|
47
|
+
if (name.includes("title")) return "title";
|
|
48
|
+
return "body";
|
|
49
|
+
}
|
|
50
|
+
function pptxTextShapes(xml) {
|
|
51
|
+
const shapes = Array.from(xml.matchAll(/<p:sp\b[\s\S]*?<\/p:sp>/g)).map((match) => {
|
|
52
|
+
const shapeXml = match[0];
|
|
53
|
+
return {
|
|
54
|
+
role: pptxShapeRole(shapeXml),
|
|
55
|
+
text: extractPptxSlideText(shapeXml)
|
|
56
|
+
};
|
|
57
|
+
}).filter((shape) => shape.text.length > 0);
|
|
58
|
+
const hasExplicitHeading = shapes.some(
|
|
59
|
+
(shape) => shape.role === "title" || shape.role === "subtitle"
|
|
60
|
+
);
|
|
61
|
+
if (!hasExplicitHeading && shapes.length === 1) {
|
|
62
|
+
return [{ ...shapes[0], role: "title" }];
|
|
63
|
+
}
|
|
64
|
+
return shapes;
|
|
65
|
+
}
|
|
66
|
+
function pptxShapeRoleQueues(xml) {
|
|
67
|
+
const roles = /* @__PURE__ */ new Map();
|
|
68
|
+
for (const shape of pptxTextShapes(xml)) {
|
|
69
|
+
const key = normalizePptxText(shape.text);
|
|
70
|
+
if (!key) continue;
|
|
71
|
+
const queue = roles.get(key) ?? [];
|
|
72
|
+
queue.push(shape.role);
|
|
73
|
+
roles.set(key, queue);
|
|
74
|
+
}
|
|
75
|
+
return roles;
|
|
76
|
+
}
|
|
77
|
+
function normalizePptxMarkdownHeadings(markdown, xml) {
|
|
78
|
+
const roles = pptxShapeRoleQueues(xml);
|
|
79
|
+
if (roles.size === 0) return markdown;
|
|
80
|
+
return cleanExtractedMarkdown(
|
|
81
|
+
markdown.split("\n").map((line) => {
|
|
82
|
+
const heading = line.match(/^#{1,6}\s+(.+?)\s*$/);
|
|
83
|
+
if (!heading) return line;
|
|
84
|
+
const text = heading[1] ?? "";
|
|
85
|
+
const queue = roles.get(normalizePptxText(text));
|
|
86
|
+
const role = queue?.shift();
|
|
87
|
+
if (!role) return line;
|
|
88
|
+
if (role === "title") return `# ${text}`;
|
|
89
|
+
if (role === "subtitle") return `## ${text}`;
|
|
90
|
+
return text;
|
|
91
|
+
}).join("\n")
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function formatPptxShapeText(shape) {
|
|
95
|
+
if (shape.role === "title") return `# ${shape.text}`;
|
|
96
|
+
if (shape.role === "subtitle") return `## ${shape.text}`;
|
|
97
|
+
return shape.text;
|
|
98
|
+
}
|
|
99
|
+
function formatPptxSlideText(xml) {
|
|
100
|
+
const shapes = pptxTextShapes(xml);
|
|
101
|
+
const nonShapeText = extractPptxSlideText(xml.replace(/<p:sp\b[\s\S]*?<\/p:sp>/g, ""));
|
|
102
|
+
if (shapes.length === 0) return nonShapeText || extractPptxSlideText(xml);
|
|
103
|
+
return [...shapes.map(formatPptxShapeText), nonShapeText].filter(Boolean).join("\n\n").trim();
|
|
104
|
+
}
|
|
33
105
|
function normalizeZipPath(path) {
|
|
34
106
|
const parts = [];
|
|
35
107
|
for (const part of path.split("/")) {
|
|
@@ -89,6 +161,42 @@ async function pptxSlidePaths(zip) {
|
|
|
89
161
|
...fallback.filter((path) => !ordered.includes(path))
|
|
90
162
|
];
|
|
91
163
|
}
|
|
164
|
+
async function buildSingleSlidePptx(slideXml) {
|
|
165
|
+
const zip = new JSZip();
|
|
166
|
+
zip.file(
|
|
167
|
+
"[Content_Types].xml",
|
|
168
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
169
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
170
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
171
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
172
|
+
<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
|
|
173
|
+
<Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
|
|
174
|
+
</Types>`
|
|
175
|
+
);
|
|
176
|
+
zip.file(
|
|
177
|
+
"_rels/.rels",
|
|
178
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
179
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
180
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
|
|
181
|
+
</Relationships>`
|
|
182
|
+
);
|
|
183
|
+
zip.file(
|
|
184
|
+
"ppt/presentation.xml",
|
|
185
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
186
|
+
<p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
187
|
+
<p:sldIdLst><p:sldId id="256" r:id="rId1"/></p:sldIdLst>
|
|
188
|
+
</p:presentation>`
|
|
189
|
+
);
|
|
190
|
+
zip.file(
|
|
191
|
+
"ppt/_rels/presentation.xml.rels",
|
|
192
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
193
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
194
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>
|
|
195
|
+
</Relationships>`
|
|
196
|
+
);
|
|
197
|
+
zip.file("ppt/slides/slide1.xml", slideXml);
|
|
198
|
+
return await zip.generateAsync({ type: "uint8array" });
|
|
199
|
+
}
|
|
92
200
|
async function extractPdfByPage(buffer) {
|
|
93
201
|
const { extractBytes } = await loadKreuzbergNative();
|
|
94
202
|
const source = await PDFDocument.load(buffer, { ignoreEncryption: true });
|
|
@@ -119,21 +227,37 @@ async function extractPdfByPage(buffer) {
|
|
|
119
227
|
async function extractPptxBySlide(buffer, mimeType) {
|
|
120
228
|
const zip = await JSZip.loadAsync(buffer);
|
|
121
229
|
const slidePaths = await pptxSlidePaths(zip);
|
|
230
|
+
const config = extractionConfigForMimeType(mimeType);
|
|
231
|
+
let nativeExtractor;
|
|
232
|
+
try {
|
|
233
|
+
nativeExtractor = await loadKreuzbergNative();
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (!slidePaths.length) throw error;
|
|
236
|
+
}
|
|
122
237
|
if (!slidePaths.length) {
|
|
123
|
-
const
|
|
124
|
-
const result = await extractBytes(
|
|
238
|
+
const result = await nativeExtractor.extractBytes(
|
|
125
239
|
new Uint8Array(buffer),
|
|
126
240
|
mimeType,
|
|
127
|
-
|
|
241
|
+
config
|
|
128
242
|
);
|
|
129
|
-
|
|
130
|
-
|
|
243
|
+
const content = cleanExtractedMarkdown(result.content);
|
|
244
|
+
postProgress({ unit: "file", current: 1, total: 1, characters: content.length });
|
|
245
|
+
return content;
|
|
131
246
|
}
|
|
132
247
|
const slides = [];
|
|
133
248
|
for (const [index, path] of slidePaths.entries()) {
|
|
134
249
|
const file = zip.file(path);
|
|
135
250
|
const xml = file ? await file.async("text") : "";
|
|
136
|
-
|
|
251
|
+
let content;
|
|
252
|
+
if (nativeExtractor) {
|
|
253
|
+
try {
|
|
254
|
+
const slideBytes = await buildSingleSlidePptx(xml);
|
|
255
|
+
const result = await nativeExtractor.extractBytes(slideBytes, mimeType, config);
|
|
256
|
+
content = normalizePptxMarkdownHeadings(cleanExtractedMarkdown(result.content), xml);
|
|
257
|
+
} catch {
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
content ||= formatPptxSlideText(xml);
|
|
137
261
|
slides.push(content);
|
|
138
262
|
postProgress({
|
|
139
263
|
unit: "slide",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veryfront/ext-document-kreuzberg",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.996",
|
|
4
4
|
"description": "Veryfront first-party extension package for ext-document-kreuzberg",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"veryfront",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"pdf-lib": "1.17.1"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"veryfront": "^0.1.
|
|
58
|
+
"veryfront": "^0.1.996"
|
|
59
59
|
},
|
|
60
60
|
"type": "module",
|
|
61
61
|
"types": "./esm/extensions/ext-document-kreuzberg/src/index.d.ts",
|