@veryfront/ext-document-kreuzberg 0.1.993 → 0.1.995

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.
@@ -1,2 +1,2 @@
1
- export declare function extractionConfigForMimeType(mimeType: string): Record<string, unknown> | undefined;
1
+ export declare function extractionConfigForMimeType(mimeType: string): Record<string, unknown>;
2
2
  //# sourceMappingURL=extraction-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extraction-config.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-document-kreuzberg/src/extraction-config.ts"],"names":[],"mappings":"AAcA,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAIjG"}
1
+ {"version":3,"file":"extraction-config.d.ts","sourceRoot":"","sources":["../../../../src/extensions/ext-document-kreuzberg/src/extraction-config.ts"],"names":[],"mappings":"AAmBA,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIrF"}
@@ -1,4 +1,8 @@
1
- const PDF_TEXT_ONLY_EXTRACTION_CONFIG = {
1
+ const MARKDOWN_EXTRACTION_CONFIG = {
2
+ outputFormat: "markdown",
3
+ };
4
+ const PDF_MARKDOWN_EXTRACTION_CONFIG = {
5
+ ...MARKDOWN_EXTRACTION_CONFIG,
2
6
  images: { extractImages: false },
3
7
  pdfOptions: {
4
8
  extractImages: false,
@@ -12,6 +16,6 @@ function normalizeMimeType(mimeType) {
12
16
  }
13
17
  export function extractionConfigForMimeType(mimeType) {
14
18
  return normalizeMimeType(mimeType) === "application/pdf"
15
- ? PDF_TEXT_ONLY_EXTRACTION_CONFIG
16
- : undefined;
19
+ ? PDF_MARKDOWN_EXTRACTION_CONFIG
20
+ : MARKDOWN_EXTRACTION_CONFIG;
17
21
  }
@@ -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,17 +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 { extractBytes } = await loadKreuzbergNative();
124
- const result = await extractBytes(new Uint8Array(buffer), mimeType);
125
- postProgress({ unit: "file", current: 1, total: 1, characters: result.content.length });
126
- return result.content;
238
+ const result = await nativeExtractor.extractBytes(
239
+ new Uint8Array(buffer),
240
+ mimeType,
241
+ config
242
+ );
243
+ const content = cleanExtractedMarkdown(result.content);
244
+ postProgress({ unit: "file", current: 1, total: 1, characters: content.length });
245
+ return content;
127
246
  }
128
247
  const slides = [];
129
248
  for (const [index, path] of slidePaths.entries()) {
130
249
  const file = zip.file(path);
131
250
  const xml = file ? await file.async("text") : "";
132
- const content = extractPptxSlideText(xml);
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);
133
261
  slides.push(content);
134
262
  postProgress({
135
263
  unit: "slide",
@@ -142,7 +270,11 @@ async function extractPptxBySlide(buffer, mimeType) {
142
270
  }
143
271
  async function extractWholeFile(buffer, mimeType) {
144
272
  const { extractBytes } = await loadKreuzbergNative();
145
- const result = await extractBytes(new Uint8Array(buffer), mimeType);
273
+ const result = await extractBytes(
274
+ new Uint8Array(buffer),
275
+ mimeType,
276
+ extractionConfigForMimeType(mimeType)
277
+ );
146
278
  postProgress({ unit: "file", current: 1, total: 1, characters: result.content.length });
147
279
  return result.content;
148
280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veryfront/ext-document-kreuzberg",
3
- "version": "0.1.993",
3
+ "version": "0.1.995",
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.993"
58
+ "veryfront": "^0.1.995"
59
59
  },
60
60
  "type": "module",
61
61
  "types": "./esm/extensions/ext-document-kreuzberg/src/index.d.ts",