@dsh-libing/knowledge-base 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/CHANGELOG.md +16 -0
- package/README.md +80 -0
- package/cordis.patch.yml +3 -0
- package/icon.svg +6 -0
- package/lib/chunks.d.ts +8 -0
- package/lib/chunks.js +197 -0
- package/lib/client-types/client/BrowsePanel.d.ts +13 -0
- package/lib/client-types/client/Icon.d.ts +4 -0
- package/lib/client-types/client/ImageReview.d.ts +20 -0
- package/lib/client-types/client/ImportPanel.d.ts +19 -0
- package/lib/client-types/client/JobsPanel.d.ts +6 -0
- package/lib/client-types/client/KnowledgePage.d.ts +7 -0
- package/lib/client-types/client/Metadata.d.ts +10 -0
- package/lib/client-types/client/QualityReport.d.ts +6 -0
- package/lib/client-types/client/SettingsPanel.d.ts +6 -0
- package/lib/client-types/client/SourceText.d.ts +3 -0
- package/lib/client-types/client/UsageGuide.d.ts +8 -0
- package/lib/client-types/client/api.d.ts +953 -0
- package/lib/client-types/client/common.d.ts +11 -0
- package/lib/client-types/client/index.d.ts +14 -0
- package/lib/client-types/client/locales.d.ts +284 -0
- package/lib/client-types/client/searchCommand.d.ts +2 -0
- package/lib/client-types/client/useKnowledge.d.ts +251 -0
- package/lib/client-types/contract.d.ts +1197 -0
- package/lib/client-types/data.d.ts +1972 -0
- package/lib/client-types/settings.d.ts +33 -0
- package/lib/client-types/shared.d.ts +95 -0
- package/lib/client.js +305 -0
- package/lib/config.d.ts +13 -0
- package/lib/config.js +13 -0
- package/lib/contract.d.ts +1197 -0
- package/lib/contract.js +18 -0
- package/lib/data.d.ts +1972 -0
- package/lib/data.js +80 -0
- package/lib/document/convert.d.ts +5 -0
- package/lib/document/convert.js +325 -0
- package/lib/document/pdf-data.d.ts +99 -0
- package/lib/document/pdf-data.js +9 -0
- package/lib/document/pdf-layout.d.ts +24 -0
- package/lib/document/pdf-layout.js +234 -0
- package/lib/document/pdf-session.d.ts +39 -0
- package/lib/document/pdf-session.js +127 -0
- package/lib/document/pdf-vision.d.ts +26 -0
- package/lib/document/pdf-vision.js +115 -0
- package/lib/document/pdf-worker.d.ts +1 -0
- package/lib/document/pdf-worker.js +210 -0
- package/lib/document/pdf.d.ts +3 -0
- package/lib/document/pdf.js +218 -0
- package/lib/document/zip.d.ts +3 -0
- package/lib/document/zip.js +59 -0
- package/lib/figures/process.d.ts +187 -0
- package/lib/figures/process.js +159 -0
- package/lib/figures/prompts.d.ts +7 -0
- package/lib/figures/prompts.js +27 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +42 -0
- package/lib/indexes.d.ts +38 -0
- package/lib/indexes.js +101 -0
- package/lib/jobs.d.ts +2806 -0
- package/lib/jobs.js +435 -0
- package/lib/models.d.ts +24 -0
- package/lib/models.js +120 -0
- package/lib/retrieval.d.ts +209 -0
- package/lib/retrieval.js +173 -0
- package/lib/routes.d.ts +3 -0
- package/lib/routes.js +152 -0
- package/lib/runtime.d.ts +48 -0
- package/lib/runtime.js +41 -0
- package/lib/settings.d.ts +33 -0
- package/lib/settings.js +19 -0
- package/lib/shared.d.ts +95 -0
- package/lib/shared.js +17 -0
- package/lib/skills.d.ts +3 -0
- package/lib/skills.js +21 -0
- package/lib/store.d.ts +409 -0
- package/lib/store.js +328 -0
- package/lib/tools.d.ts +3 -0
- package/lib/tools.js +72 -0
- package/lib/util.d.ts +25 -0
- package/lib/util.js +89 -0
- package/package.json +125 -0
- package/skills/libing-kb-build/SKILL.md +10 -0
- package/skills/libing-kb-search/SKILL.md +10 -0
- package/skills/libing-knowledge-base/SKILL.md +15 -0
- package/skills/libing-knowledge-base/references/build.md +23 -0
- package/skills/libing-knowledge-base/references/search.md +21 -0
package/lib/data.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Persisted schemas validate untrusted receipts and preserve the previous format's versioned references. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { settingsSchema } from "./settings.js";
|
|
4
|
+
export const idSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{0,95}$/u);
|
|
5
|
+
export const routeSchema = z.object({ provider: z.string().min(1), model: z.string().min(1) });
|
|
6
|
+
export const bboxSchema = z.tuple([z.number().finite(), z.number().finite(), z.number().finite(), z.number().finite()]);
|
|
7
|
+
export const sourceSchema = z.object({ page: z.number().int().positive(), pageLabel: z.string().nullable().optional(),
|
|
8
|
+
width: z.number().nullable().optional(), height: z.number().nullable().optional(), bbox: bboxSchema.nullable(),
|
|
9
|
+
coordinateSystem: z.literal('viewport-points-top-left'), rotation: z.number().optional(), method: z.string(), caption: z.string().optional(),
|
|
10
|
+
});
|
|
11
|
+
export const fullSourceSchema = sourceSchema.extend({ locations: z.array(sourceSchema).optional() });
|
|
12
|
+
export const rangeSchema = z.object({ start: z.number().int().positive(), end: z.number().int().positive(), count: z.number().int().positive(), total: z.number().int().positive(), partial: z.boolean() });
|
|
13
|
+
export const warningSchema = z.object({ code: z.string(), message: z.string(), blocking: z.boolean(), page: z.number().optional(), kind: z.string().optional(), snippet: z.string().optional(), reason: z.string().optional() });
|
|
14
|
+
export const assetSchema = z.object({ assetId: idSchema, mime: z.string(), path: z.string(), bytes: z.number().int().nonnegative(), origin: z.string(), source: fullSourceSchema.optional() });
|
|
15
|
+
export const FIGURE_TYPES = ['architecture', 'pinout', 'schematic', 'timing', 'flowchart', 'table', 'curve', 'other', 'decoration'];
|
|
16
|
+
export const figureDataSchema = z.object({ types: z.array(z.enum(FIGURE_TYPES)).min(1), visibleLabels: z.array(z.string()), description: z.string().min(1),
|
|
17
|
+
relations: z.array(z.object({ from: z.string(), to: z.string(), label: z.string() })), uncertainties: z.array(z.string()), needsReview: z.boolean(),
|
|
18
|
+
contextNotes: z.string().optional(), pins: z.array(z.json()).optional(), timingConstraints: z.array(z.json()).optional(), tableRows: z.array(z.json()).optional() });
|
|
19
|
+
export const overlaySchema = z.lazy(() => figureDataSchema.partial().extend({ ignore: z.boolean().optional(), skip: z.boolean().optional(), updatedAt: z.string().optional(), previous: overlaySchema.nullable().optional() }));
|
|
20
|
+
export const figureSchema = figureDataSchema.partial().extend({ status: z.enum(['generated', 'failed', 'skipped', 'ignored', 'decoration', 'reviewed']),
|
|
21
|
+
assetId: idSchema, occurrenceId: idSchema, caption: z.string().default(''), cacheKey: z.string(), overlayKey: z.string(), overlayHash: z.string(),
|
|
22
|
+
humanReviewed: z.boolean().default(false), needsReview: z.boolean().default(false), error: z.string().optional(),
|
|
23
|
+
classification: z.object({ types: z.array(z.enum(FIGURE_TYPES)), reason: z.string() }).optional(), width: z.number().optional(), height: z.number().optional(),
|
|
24
|
+
model: routeSchema.optional(), promptVersion: z.string().optional(), context: z.object({ headingPath: z.array(z.string()), caption: z.string().optional(), nearby: z.array(z.string()) }).optional(),
|
|
25
|
+
source: fullSourceSchema.optional(), nativeLabels: z.array(z.string()).optional(), refreshToken: z.string().optional(), ignore: z.boolean().optional(), skip: z.boolean().optional(), previous: overlaySchema.nullable().optional(), updatedAt: z.string().optional() });
|
|
26
|
+
export const cellSchema = z.object({ text: z.string(), colspan: z.number().int().positive(), rowspan: z.number().int().positive(), source: fullSourceSchema.optional(), spanIds: z.array(z.string()).optional(), ocr: z.boolean().optional() });
|
|
27
|
+
export const blockSchema = z.object({ type: z.enum(['heading', 'paragraph', 'table', 'figure', 'caption', 'list', 'note', 'code', 'formula']),
|
|
28
|
+
blockId: z.string().default(''), text: z.string().default(''), level: z.number().int().min(1).max(32).default(1), rows: z.array(z.array(cellSchema)).default([]),
|
|
29
|
+
caption: z.string().default(''), assetId: z.string().default(''), occurrenceId: z.string().default(''), spanIds: z.array(z.string()).default([]),
|
|
30
|
+
anchor: z.string().nullable().optional(), markdown: z.string().optional(), tableRef: z.string().optional(), figureRef: z.string().optional(),
|
|
31
|
+
source: fullSourceSchema.optional(), sourcePreviewAssetId: z.string().optional(), sourcePreviewAssetIds: z.array(z.string()).optional(),
|
|
32
|
+
nativeLabels: z.array(z.string()).default([]), figure: figureSchema.optional(), part: z.number().optional(), oversizedReason: z.string().optional(),
|
|
33
|
+
needsReview: z.boolean().optional(), headingBasis: z.string().optional(), bookmark: z.string().optional(), layoutBasis: z.string().optional(),
|
|
34
|
+
openEnd: z.boolean().optional(), tableCaption: z.boolean().optional(), ocr: z.boolean().optional(), mergeRemove: z.boolean().optional(),
|
|
35
|
+
localIndex: z.number().optional(), captionFigureIndex: z.number().optional() });
|
|
36
|
+
export function block(value) { return blockSchema.parse(value); }
|
|
37
|
+
export const pageAuditSchema = z.object({ page: z.number(), width: z.number().optional(), height: z.number().optional(), pageLabel: z.string().nullable().optional(),
|
|
38
|
+
status: z.enum(['needs-review', 'navigation', 'parsed', 'failed']), ocr: z.boolean(), spanCount: z.number().optional(), artifactCount: z.number().optional(),
|
|
39
|
+
excluded: z.array(z.object({ kind: z.string(), text: z.string().optional(), bbox: bboxSchema.nullable() })).optional(),
|
|
40
|
+
tables: z.number(), figures: z.number(), headings: z.array(z.object({ text: z.string(), basis: z.string().optional(), level: z.number() })),
|
|
41
|
+
previewAssetId: z.string().nullable().optional(), error: z.string().optional() });
|
|
42
|
+
export const auditSchema = z.object({ pages: z.array(pageAuditSchema).default([]), outlineCount: z.number().default(0), matchedBookmarks: z.number().default(0),
|
|
43
|
+
convertedFigures: z.number(), convertedTables: z.number(), warnings: z.array(warningSchema).optional(), styleLevels: z.record(z.string(), z.number()).optional(),
|
|
44
|
+
headingLevels: z.record(z.string(), z.number()).optional(), outlineLevels: z.record(z.string(), z.number()).optional(),
|
|
45
|
+
expectedFigures: z.number().optional(), expectedTables: z.number().optional(), expectedHeadings: z.array(z.string()).optional(), textBoxes: z.number().optional(),
|
|
46
|
+
footnoteRefs: z.number().optional(), anchors: z.array(z.string()).optional(), rawMediaCount: z.number().optional() });
|
|
47
|
+
export const documentSchema = z.object({ formatVersion: z.literal(1), sourceFormat: z.enum(['docx', 'pdf']).optional(), pageCount: z.number().optional(),
|
|
48
|
+
selectedPages: rangeSchema.optional(), parserVersion: z.string().optional(), sourceHash: z.string().optional(), blocks: z.array(blockSchema), assets: z.array(assetSchema),
|
|
49
|
+
internalRefs: z.array(z.object({ target: z.string(), label: z.string() })), warnings: z.array(warningSchema), audit: auditSchema });
|
|
50
|
+
export const nodeSchema = z.object({ nodeId: idSchema, title: z.string(), level: z.number(), parentId: z.string().nullable(), headingPath: z.array(z.string()), children: z.array(z.string()), anchor: z.string().nullable().optional(), indexPath: z.string().optional() });
|
|
51
|
+
export const segmentSchema = z.object({ text: z.string(), blockIds: z.array(z.string()), type: blockSchema.shape.type, sourceLocations: z.array(fullSourceSchema).optional(), startLine: z.number().optional(), endLine: z.number().optional() });
|
|
52
|
+
export const chunkSchema = z.object({ chunkId: idSchema, docId: idSchema, sourceHash: z.string(), sourceVersion: z.string(), title: z.string(), nodeId: idSchema, headingPath: z.array(z.string()),
|
|
53
|
+
blockRange: z.tuple([z.string(), z.string()]), assetRefs: z.array(z.string()), tableRefs: z.array(z.string()), relatedChunks: z.array(z.string()), reviewStatus: z.enum(['needs-review', 'generated', 'source']),
|
|
54
|
+
path: z.string(), segments: z.array(segmentSchema), anchors: z.array(z.string()), sourceFormat: z.enum(['docx', 'pdf']).optional(), sourceLocations: z.array(fullSourceSchema).optional(),
|
|
55
|
+
pageRange: z.tuple([z.number(), z.number()]).optional(), selectedPages: rangeSchema.optional(), oversizedReason: z.string().optional(),
|
|
56
|
+
fullChars: z.number(), bodyChars: z.number(), contentHash: z.string(), summary: z.string().default(''), summaryFallback: z.boolean().optional(), keywords: z.array(z.string()).default([]) });
|
|
57
|
+
export const infoSchema = z.object({ docId: idSchema, title: z.string(), filename: z.string(), sourceHash: z.string(), sourceVersion: z.string(), sourceFormat: z.enum(['docx', 'pdf']).optional(), pageCount: z.number().optional(), selectedPages: rangeSchema.optional(), parserVersion: z.string().optional(), audit: auditSchema.optional() });
|
|
58
|
+
export const entrySchema = z.object({ title: z.string().optional(), path: z.string().optional(), docId: z.string().optional(), nodeId: z.string().optional(), chunkId: z.string().optional(), summary: z.string().optional(), fullChars: z.number().optional(), keywords: z.array(z.string()).optional(), reviewStatus: z.string().optional(), pdfScope: z.string().optional(), pageRange: z.tuple([z.number(), z.number()]).optional() });
|
|
59
|
+
export const catalogSchema = z.object({ catalogId: z.string(), path: z.string(), title: z.string(), pages: z.array(z.object({ path: z.string(), content: z.string(), entries: z.array(entrySchema), next: z.string().nullable() })) });
|
|
60
|
+
export const entitySchema = z.object({ label: z.string(), docId: z.string(), chunkId: z.string(), path: z.string(), headingPath: z.array(z.string()) });
|
|
61
|
+
export const usageSchema = z.object({ calls: z.number().int().nonnegative(), tokens: z.number().nonnegative(), reportedCalls: z.number().int().nonnegative(), records: z.array(z.json()) });
|
|
62
|
+
export const reportSchema = z.object({ revision: z.string(), sourceHash: z.string(), issues: z.array(warningSchema), documents: z.number(), blocks: z.number(), figures: z.number(), ignoredFigures: z.number(), unreviewedFigures: z.number(), chunks: z.number(), withinTarget: z.number(), oversized: z.array(z.object({ chunkId: z.string(), chars: z.number(), reason: z.string() })), usage: usageSchema, targetChars: z.number(), pdf: z.object({ pageCount: z.number(), selectedPages: rangeSchema, outlineCount: z.number(), matchedBookmarks: z.number(), ocrPages: z.number(), pages: z.array(pageAuditSchema) }).optional() });
|
|
63
|
+
export const publishedDocumentSchema = infoSchema.extend({ chunkTargetChars: z.number().optional(), nodes: z.array(nodeSchema), chunks: z.array(chunkSchema), assets: z.array(assetSchema), figures: z.array(figureSchema), coverageIncomplete: z.boolean().optional(), warnings: z.array(warningSchema).optional() });
|
|
64
|
+
export const manifestSchema = z.object({ formatVersion: z.literal(1), revision: idSchema, buildKey: z.string(), createdAt: z.string(), documents: z.array(publishedDocumentSchema), catalogs: z.record(z.string(), catalogSchema), entities: z.record(z.string(), z.array(entitySchema)), coverageIncomplete: z.boolean(), config: z.object({ chunkTargetChars: z.number() }), report: reportSchema });
|
|
65
|
+
export const jobStateSchema = z.enum(['queued', 'preflight', 'converting', 'describing', 'splitting', 'indexing', 'validating', 'publishing', 'needs-review', 'paused-budget', 'cancelled', 'failed', 'ready']);
|
|
66
|
+
export const jobSchema = z.object({ jobId: idSchema, kbId: idSchema, docId: idSchema, title: z.string(), sourceVersion: z.string(), filename: z.string(), sourceFormat: z.enum(['docx', 'pdf']).default('docx'),
|
|
67
|
+
sourceOriginalHash: z.string(), sourceHash: z.string().optional(), buildKey: z.string(), config: settingsSchema, routes: z.object({ vision: routeSchema.nullable(), summary: routeSchema.nullable() }),
|
|
68
|
+
schema: z.string(), schemaHash: z.string(), overlays: z.record(z.string(), overlaySchema), sessionId: z.string().nullable(), state: jobStateSchema, createdAt: z.string(), updatedAt: z.string().optional(),
|
|
69
|
+
progress: z.object({ totalPages: z.number().optional(), parsedPages: z.number().optional(), ocrPages: z.number().optional(), tables: z.number().optional(), blocks: z.number().optional(), currentPage: z.number().optional(),
|
|
70
|
+
totalFigures: z.number().optional(), completedFigures: z.number().optional(), inflightFigures: z.array(z.object({ occurrenceId: z.string(), caption: z.string() })).optional(),
|
|
71
|
+
totalChunks: z.number().optional(), indexedChunks: z.number().optional(), currentChunk: z.object({ title: z.string(), headingPath: z.array(z.string()), chunkId: z.string() }).optional() }),
|
|
72
|
+
usage: usageSchema, failures: z.array(z.object({ at: z.string(), stage: jobStateSchema, error: z.string() })), events: z.array(z.object({ at: z.string(), state: jobStateSchema })).optional(),
|
|
73
|
+
figureRefresh: z.object({ occurrenceId: z.string(), token: z.string() }).nullable().optional(), visionCapability: z.string().optional(), parserVersion: z.string().optional(),
|
|
74
|
+
pageStart: z.number().optional(), pageEnd: z.number().optional(), pdf: z.object({ pageCount: z.number(), selectedPages: rangeSchema, outlineCount: z.number().optional() }).optional(),
|
|
75
|
+
revision: z.string().optional(), report: reportSchema.optional(), outputPath: z.string().optional(), error: z.string().nullable().optional(), recoveredAt: z.string().optional(), finishedAt: z.string().optional(), cancelRequested: z.boolean().optional() });
|
|
76
|
+
export const librarySchema = z.object({ kbId: idSchema, name: z.string(), createdAt: z.string(), deletedAt: z.string().optional(), purge: z.object({ startedAt: z.string(), jobIds: z.array(idSchema), error: z.string().optional() }).optional() });
|
|
77
|
+
export const registrySchema = z.object({ formatVersion: z.literal(1), kbs: z.array(librarySchema), activities: z.array(z.object({ eventId: z.string(), at: z.string(), action: z.enum(['create', 'trash', 'restore', 'purge']), kbId: z.string(), name: z.string() })).default([]) });
|
|
78
|
+
export const currentSchema = z.object({ revision: idSchema, publishedAt: z.string(), buildKey: z.string(), coverageIncomplete: z.boolean() });
|
|
79
|
+
export const uploadSchema = z.object({ receiptId: idSchema, filename: z.string(), createdAt: z.string(), bytes: z.number(), hash: z.string() });
|
|
80
|
+
export const pageReceiptSchema = z.object({ blocks: z.array(blockSchema), assets: z.array(assetSchema), warnings: z.array(warningSchema), audit: pageAuditSchema });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Settings } from '../config.ts';
|
|
2
|
+
import type { Block, Asset, Document } from '../data.ts';
|
|
3
|
+
export declare function convertDocx(buffer: Buffer, destination: string, config: Settings, signal?: AbortSignal): Promise<Document>;
|
|
4
|
+
export declare function blockMarkdown(block: Block, assets?: Asset[], assetPrefix?: string): string;
|
|
5
|
+
export declare function legacyToDocx(file: string, executable: string, signal?: AbortSignal): Promise<Buffer>;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { block as makeBlock, documentSchema } from "../data.js";
|
|
3
|
+
import { required } from "../util.js";
|
|
4
|
+
import mammoth from 'mammoth';
|
|
5
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
6
|
+
import { parseFragment } from 'parse5';
|
|
7
|
+
import { atomicWrite, checkAbort, escapeHtml, hash, mdText } from "../util.js";
|
|
8
|
+
import { unpackDocx } from "./zip.js";
|
|
9
|
+
import { join, posix } from 'node:path';
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
const parser = new XMLParser({ ignoreAttributes: false, removeNSPrefix: true, parseTagValue: false, trimValues: false });
|
|
14
|
+
const xmlSchema = z.lazy(() => z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(xmlSchema), z.record(z.string(), xmlSchema)]));
|
|
15
|
+
const list = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
16
|
+
const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
17
|
+
function child(value, ...keys) {
|
|
18
|
+
for (const key of keys)
|
|
19
|
+
value = object(value)[key];
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function xmlString(value) { return typeof value === 'string' || typeof value === 'number' ? String(value) : ''; }
|
|
23
|
+
function parseXml(bytes) {
|
|
24
|
+
const xml = Buffer.from(bytes).toString('utf8');
|
|
25
|
+
if (/<!DOCTYPE|<!ENTITY/iu.test(xml))
|
|
26
|
+
throw new Error('不允许 DOCX XML 实体定义');
|
|
27
|
+
return xmlSchema.parse(parser.parse(xml));
|
|
28
|
+
}
|
|
29
|
+
function visit(value, fn) {
|
|
30
|
+
for (const [key, c] of Object.entries(object(value)))
|
|
31
|
+
for (const item of list(c)) {
|
|
32
|
+
fn(key, item);
|
|
33
|
+
visit(item, fn);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function wordText(value) {
|
|
37
|
+
let result = '';
|
|
38
|
+
visit(value, (key, v) => {
|
|
39
|
+
if (key === 't')
|
|
40
|
+
result += typeof v === 'object' ? xmlString(child(v, '#text')) : xmlString(v);
|
|
41
|
+
});
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
const normalize = (value) => value.replace(/\s/gu, '');
|
|
45
|
+
function audit(files) {
|
|
46
|
+
const doc = parseXml(required(files['word/document.xml'])), styles = files['word/styles.xml'] ? parseXml(files['word/styles.xml']) : {};
|
|
47
|
+
const warnings = [], styleLevels = {}, headingLevels = {}, outlineLevels = {}, anchors = new Set();
|
|
48
|
+
const styleRows = list(child(styles, 'styles', 'style'));
|
|
49
|
+
const resolveLevel = (styleId, seen = new Set()) => {
|
|
50
|
+
if (seen.has(styleId) || !styleId)
|
|
51
|
+
return;
|
|
52
|
+
seen.add(styleId);
|
|
53
|
+
const row = styleRows.find(v => xmlString(child(v, '@_styleId')) === styleId);
|
|
54
|
+
if (!row)
|
|
55
|
+
return;
|
|
56
|
+
const explicit = child(row, 'pPr', 'outlineLvl', '@_val'), name = xmlString(child(row, 'name', '@_val')) || styleId, common = /^(?:heading|标题)\s*([1-9])$/iu.exec(name);
|
|
57
|
+
return explicit !== undefined && Number(explicit) < 9 ? Number(explicit) + 1 : common ? Number(common[1]) : resolveLevel(xmlString(child(row, 'basedOn', '@_val')), seen);
|
|
58
|
+
};
|
|
59
|
+
for (const row of styleRows) {
|
|
60
|
+
const styleId = xmlString(child(row, '@_styleId')), level = resolveLevel(styleId);
|
|
61
|
+
if (level)
|
|
62
|
+
styleLevels[styleId] = level;
|
|
63
|
+
}
|
|
64
|
+
let expectedFigures = 0, expectedTables = 0, textBoxes = 0, footnoteRefs = 0;
|
|
65
|
+
const expectedHeadings = [];
|
|
66
|
+
visit(doc, (key, value) => {
|
|
67
|
+
if (key === 'p') {
|
|
68
|
+
const outline = child(value, 'pPr', 'outlineLvl', '@_val'), style = xmlString(child(value, 'pPr', 'pStyle', '@_val')), level = outline !== undefined && Number(outline) < 9 ? Number(outline) + 1 : styleLevels[style];
|
|
69
|
+
if (outline !== undefined && Number(outline) < 9)
|
|
70
|
+
outlineLevels[normalize(wordText(value))] = Number(outline) + 1;
|
|
71
|
+
if (level && !/^TOC/iu.test(style)) {
|
|
72
|
+
const title = wordText(value);
|
|
73
|
+
if (title) {
|
|
74
|
+
expectedHeadings.push(title);
|
|
75
|
+
headingLevels[normalize(title)] = level;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if ((key === 'blip' && child(value, '@_embed')) || (key === 'imagedata' && child(value, '@_id')))
|
|
80
|
+
expectedFigures++;
|
|
81
|
+
if (key === 'tbl')
|
|
82
|
+
expectedTables++;
|
|
83
|
+
if (key === 'txbxContent')
|
|
84
|
+
textBoxes++;
|
|
85
|
+
if (key === 'footnoteReference' || key === 'endnoteReference')
|
|
86
|
+
footnoteRefs++;
|
|
87
|
+
if (key === 'bookmarkStart' && child(value, '@_name'))
|
|
88
|
+
anchors.add(xmlString(child(value, '@_name')));
|
|
89
|
+
if (['graphicData', 'object', 'pict', 'oMath', 'oMathPara', 'ins', 'del'].includes(key)) {
|
|
90
|
+
if (key === 'graphicData' && /\/picture$/iu.test(xmlString(child(value, '@_uri'))))
|
|
91
|
+
return;
|
|
92
|
+
if (key === 'pict' && JSON.stringify(value).includes('imagedata'))
|
|
93
|
+
return;
|
|
94
|
+
const reason = ['ins', 'del'].includes(key) ? '文档含修订,请核对转换结果并明确接受版本' : '复杂对象需要核对预览或补图';
|
|
95
|
+
warnings.push({ code: 'unsupported-object', message: `${key}:${reason}`, kind: key, snippet: wordText(value).slice(0, 120), reason, blocking: true });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
for (const [name, bytes] of Object.entries(files))
|
|
99
|
+
if (/word\/(?:header|footer)\d*\.xml$/u.test(name)) {
|
|
100
|
+
const t = wordText(parseXml(bytes)).trim();
|
|
101
|
+
if (t)
|
|
102
|
+
warnings.push({ code: 'header-footer', message: `${name} 页眉/页脚未作为正文切分:${t.slice(0, 160)}`, blocking: false });
|
|
103
|
+
}
|
|
104
|
+
return { warnings, styleLevels, headingLevels, outlineLevels, expectedFigures, expectedTables, expectedHeadings, textBoxes, footnoteRefs, anchors: [...anchors] };
|
|
105
|
+
}
|
|
106
|
+
const children = (node) => 'childNodes' in node ? node.childNodes : [];
|
|
107
|
+
const tag = (node) => 'tagName' in node ? node.tagName : '';
|
|
108
|
+
function isMammothNode(value) {
|
|
109
|
+
if (typeof value !== 'object' || value === null || !('type' in value) || typeof value.type !== 'string')
|
|
110
|
+
return false;
|
|
111
|
+
for (const [key, field] of Object.entries(value))
|
|
112
|
+
if (['styleId', 'styleName', 'value'].includes(key) && field !== null && field !== undefined && typeof field !== 'string')
|
|
113
|
+
return false;
|
|
114
|
+
return !('children' in value) || value.children === undefined || Array.isArray(value.children) && value.children.every(isMammothNode);
|
|
115
|
+
}
|
|
116
|
+
function attr(node, key) { return 'attrs' in node ? node.attrs.find(a => a.name === key)?.value : undefined; }
|
|
117
|
+
function htmlText(node) { return 'value' in node ? node.value : children(node).map(htmlText).join(''); }
|
|
118
|
+
function safeLink(value) { return /^(?:https?:|mailto:|#)/i.test(value || '') ? value : null; }
|
|
119
|
+
function inline(node, internalRefs) {
|
|
120
|
+
if ('value' in node)
|
|
121
|
+
return node.value.replace(/([\\`*[\]<>])/g, '\\$1');
|
|
122
|
+
const t = tag(node), body = children(node).map(n => inline(n, internalRefs)).join('');
|
|
123
|
+
if (['script', 'style', 'iframe', 'object'].includes(t))
|
|
124
|
+
return '';
|
|
125
|
+
if (t === 'img')
|
|
126
|
+
return `\u0001${attr(node, 'src') || ''}\u0001`;
|
|
127
|
+
if (t === 'br')
|
|
128
|
+
return '\n';
|
|
129
|
+
if (t === 'strong' || t === 'b')
|
|
130
|
+
return `**${body}**`;
|
|
131
|
+
if (t === 'em' || t === 'i')
|
|
132
|
+
return `*${body}*`;
|
|
133
|
+
if (t === 'sup' || t === 'sub')
|
|
134
|
+
return `<${t}>${body}</${t}>`;
|
|
135
|
+
if (t === 'a') {
|
|
136
|
+
const href = safeLink(attr(node, 'href'));
|
|
137
|
+
if (href?.startsWith('#'))
|
|
138
|
+
internalRefs.push({ target: href.slice(1), label: htmlText(node) });
|
|
139
|
+
return href ? `[${body}](${href.replace(/[()\s]/g, ch => encodeURIComponent(ch))})` : body;
|
|
140
|
+
}
|
|
141
|
+
return body;
|
|
142
|
+
}
|
|
143
|
+
function tableCells(table) {
|
|
144
|
+
const rows = [];
|
|
145
|
+
const walk = (node) => {
|
|
146
|
+
if (tag(node) === 'tr')
|
|
147
|
+
rows.push(children(node).filter(c => ['td', 'th'].includes(tag(c))).map(cell => ({
|
|
148
|
+
text: htmlText(cell).trim(), colspan: Math.max(1, Math.min(100, Number(attr(cell, 'colspan')) || 1)), rowspan: Math.max(1, Math.min(1000, Number(attr(cell, 'rowspan')) || 1)),
|
|
149
|
+
})));
|
|
150
|
+
else
|
|
151
|
+
for (const child of children(node))
|
|
152
|
+
walk(child);
|
|
153
|
+
};
|
|
154
|
+
walk(table);
|
|
155
|
+
return rows;
|
|
156
|
+
}
|
|
157
|
+
function tableHtml(rows) {
|
|
158
|
+
return '<table>\n' + rows.map((row, i) => '<tr>' + row.map(cell => `<${i === 0 ? 'th' : 'td'}${cell.colspan > 1 ? ` colspan="${cell.colspan}"` : ''}${cell.rowspan > 1 ? ` rowspan="${cell.rowspan}"` : ''}>${escapeHtml(cell.text).replace(/\n/g, '<br>')}</${i === 0 ? 'th' : 'td'}>`).join('') + '</tr>').join('\n') + '\n</table>';
|
|
159
|
+
}
|
|
160
|
+
export async function convertDocx(buffer, destination, config, signal) {
|
|
161
|
+
checkAbort(signal);
|
|
162
|
+
const files = unpackDocx(buffer, config), originalAudit = audit(files);
|
|
163
|
+
const assets = [], warnings = [...originalAudit.warnings];
|
|
164
|
+
const mimeExtension = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/webp': 'webp', 'image/bmp': 'bmp', 'image/tiff': 'tif', 'image/x-emf': 'emf', 'image/x-wmf': 'wmf', 'image/svg+xml': 'svg' };
|
|
165
|
+
const addAsset = async (data, mime, origin) => {
|
|
166
|
+
const assetId = `a-${hash(data).slice(0, 20)}`;
|
|
167
|
+
let asset = assets.find(a => a.assetId === assetId);
|
|
168
|
+
if (!asset) {
|
|
169
|
+
asset = { assetId, mime, path: `assets/${assetId}.${mimeExtension[mime] || 'bin'}`, bytes: data.length, origin };
|
|
170
|
+
assets.push(asset);
|
|
171
|
+
await atomicWrite(join(destination, asset.path), data);
|
|
172
|
+
}
|
|
173
|
+
return asset;
|
|
174
|
+
};
|
|
175
|
+
const rawMedia = Object.entries(files).filter(([name]) => name.startsWith('word/media/'));
|
|
176
|
+
for (const [name, bytes] of rawMedia) {
|
|
177
|
+
const ext = (name.split('.').at(-1) || '').toLowerCase(), mime = Object.entries(mimeExtension).find(([, x]) => x === ext || ext === 'jpeg' && x === 'jpg')?.[0] || 'application/octet-stream';
|
|
178
|
+
await addAsset(Buffer.from(bytes), mime, name);
|
|
179
|
+
}
|
|
180
|
+
const result = await mammoth.convertToHtml({ buffer }, {
|
|
181
|
+
externalFileAccess: false, includeDefaultStyleMap: true,
|
|
182
|
+
styleMap: Array.from({ length: 9 }, (_, i) => `p[style-name='DSHHeading${i + 1}'] => h${i + 1}:fresh`),
|
|
183
|
+
transformDocument(document) {
|
|
184
|
+
if (!isMammothNode(document))
|
|
185
|
+
throw new Error('Word 转换器文档节点无效');
|
|
186
|
+
const walk = (node) => {
|
|
187
|
+
if (node.type === 'paragraph' && !/^TOC/i.test(node.styleId || '')) {
|
|
188
|
+
const level = originalAudit.styleLevels[node.styleId || ''] || originalAudit.outlineLevels[normalize((node.children || []).map(c => c.value || (c.children || []).map(x => x.value || '').join('')).join(''))];
|
|
189
|
+
if (level) {
|
|
190
|
+
node.styleName = `DSHHeading${level}`;
|
|
191
|
+
node.styleId = `DSHHeading${level}`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const child of node.children || [])
|
|
195
|
+
walk(child);
|
|
196
|
+
};
|
|
197
|
+
walk(document);
|
|
198
|
+
return document;
|
|
199
|
+
},
|
|
200
|
+
convertImage: mammoth.images.imgElement(async (image) => {
|
|
201
|
+
checkAbort(signal);
|
|
202
|
+
const asset = await addAsset(await image.readAsBuffer(), image.contentType, 'mammoth');
|
|
203
|
+
return { src: `asset:${asset.assetId}` };
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
checkAbort(signal);
|
|
207
|
+
warnings.push(...result.messages.map(m => ({ code: 'converter', message: m.message,
|
|
208
|
+
// Unmapped styles lose formatting, not paragraph text; heading coverage is audited separately.
|
|
209
|
+
blocking: !/^Unrecognised (?:paragraph|run) style:/i.test(m.message) && /image|unrecognised|unsupported/i.test(m.message),
|
|
210
|
+
})));
|
|
211
|
+
const fragment = parseFragment(result.value), blocks = [], internalRefs = [];
|
|
212
|
+
let figureCount = 0;
|
|
213
|
+
const add = (value) => { blocks.push(makeBlock({ blockId: `b-${String(blocks.length + 1).padStart(6, '0')}`, ...value })); return required(blocks.at(-1)); };
|
|
214
|
+
const addInline = (node, type = 'paragraph', prefix = '') => {
|
|
215
|
+
const body = inline(node, internalRefs), pieces = body.split(String.fromCharCode(1)).filter(Boolean);
|
|
216
|
+
for (const piece of pieces) {
|
|
217
|
+
if (piece.startsWith('asset:')) {
|
|
218
|
+
figureCount++;
|
|
219
|
+
add({ type: 'figure', assetId: piece.slice(6), occurrenceId: `fig-${String(figureCount).padStart(5, '0')}`, caption: '', text: '' });
|
|
220
|
+
}
|
|
221
|
+
else if (piece.trim())
|
|
222
|
+
add({ type, text: prefix + piece.trim(), anchor: attr(node, 'id') || null });
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const walk = (node, depth = 0) => {
|
|
226
|
+
const t = tag(node);
|
|
227
|
+
if (/^h[1-9]$/.test(t))
|
|
228
|
+
add({ type: 'heading', level: Number(t[1]), text: htmlText(node).trim(), anchor: attr(node, 'id') || null });
|
|
229
|
+
else if (t === 'table') {
|
|
230
|
+
const rows = tableCells(node), table = add({ type: 'table', rows, text: rows.map(row => row.map(c => c.text).join(' | ')).join('\n'), markdown: tableHtml(rows) });
|
|
231
|
+
const scan = (n) => {
|
|
232
|
+
if (tag(n) === 'img') {
|
|
233
|
+
const assetId = (attr(n, 'src') || '').replace(/^asset:/, '');
|
|
234
|
+
figureCount++;
|
|
235
|
+
add({ type: 'figure', assetId, occurrenceId: `fig-${String(figureCount).padStart(5, '0')}`, caption: '', tableRef: table.blockId, text: '' });
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
for (const c of children(n))
|
|
239
|
+
scan(c);
|
|
240
|
+
};
|
|
241
|
+
scan(node);
|
|
242
|
+
}
|
|
243
|
+
else if (t === 'p') {
|
|
244
|
+
const t = htmlText(node).trim(), previous = blocks.at(-1);
|
|
245
|
+
if (/^(?:图|Figure)\s*[A-Za-z]?\d[\d.\-–]*/i.test(t) && previous?.type === 'figure') {
|
|
246
|
+
previous.caption = t;
|
|
247
|
+
add({ type: 'caption', text: t, figureRef: previous.occurrenceId });
|
|
248
|
+
}
|
|
249
|
+
else
|
|
250
|
+
addInline(node, attr(node, 'id')?.startsWith('footnote') ? 'note' : 'paragraph');
|
|
251
|
+
}
|
|
252
|
+
else if (t === 'li') {
|
|
253
|
+
for (const child of children(node)) {
|
|
254
|
+
if (['ul', 'ol'].includes(tag(child)))
|
|
255
|
+
walk(child, depth + 1);
|
|
256
|
+
else
|
|
257
|
+
addInline(child, 'list', ' '.repeat(depth) + '- ');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (t === 'img')
|
|
261
|
+
addInline(node);
|
|
262
|
+
else if (!['script', 'style', 'iframe', 'object'].includes(t))
|
|
263
|
+
for (const child of children(node))
|
|
264
|
+
walk(child, depth);
|
|
265
|
+
};
|
|
266
|
+
for (const node of fragment.childNodes)
|
|
267
|
+
walk(node);
|
|
268
|
+
if (!blocks.some(b => b.text || b.type === 'figure'))
|
|
269
|
+
throw new Error('未能提取正文');
|
|
270
|
+
if (!blocks.some(b => b.type === 'heading'))
|
|
271
|
+
warnings.push({ code: 'no-headings', message: '未识别可靠标题,按正文段落分组', blocking: false });
|
|
272
|
+
if (figureCount < originalAudit.expectedFigures)
|
|
273
|
+
warnings.push({ code: 'missing-figures', message: `正文引用 ${originalAudit.expectedFigures} 张图片,转换得到 ${figureCount} 个位置`, blocking: true });
|
|
274
|
+
if (blocks.filter(b => b.type === 'table').length < originalAudit.expectedTables)
|
|
275
|
+
warnings.push({ code: 'missing-tables', message: '表格数量未能覆盖原文,需核对嵌套表格', blocking: true });
|
|
276
|
+
const headings = blocks.filter(b => b.type === 'heading').map(b => normalize(b.text));
|
|
277
|
+
for (const title of originalAudit.expectedHeadings)
|
|
278
|
+
if (!headings.includes(normalize(title)))
|
|
279
|
+
warnings.push({ code: 'missing-heading', message: `未转换标题:${title}`, blocking: true });
|
|
280
|
+
const anchors = new Set(blocks.filter(b => b.anchor).map(b => b.anchor));
|
|
281
|
+
for (const ref of internalRefs)
|
|
282
|
+
if (!anchors.has(ref.target))
|
|
283
|
+
warnings.push({ code: 'unresolved-reference', message: `未定位内部引用:${ref.label} → ${ref.target}`, blocking: false });
|
|
284
|
+
const rawText = (await mammoth.extractRawText({ buffer })).value;
|
|
285
|
+
// A token multiset catches omitted paragraphs without claiming Word page positions.
|
|
286
|
+
const normalizedOutput = normalize(htmlText(fragment));
|
|
287
|
+
for (const paragraph of rawText.split(/\n\s*\n/).map(normalize).filter(x => x.length > 12)) {
|
|
288
|
+
if (!normalizedOutput.includes(paragraph))
|
|
289
|
+
warnings.push({ code: 'text-coverage', message: `需要核对未完整匹配的正文:${paragraph.slice(0, 100)}`, blocking: true });
|
|
290
|
+
}
|
|
291
|
+
const document = documentSchema.parse({ formatVersion: 1, blocks, assets, internalRefs, warnings, audit: { ...originalAudit, rawMediaCount: rawMedia.length, convertedFigures: figureCount, convertedTables: blocks.filter(b => b.type === 'table').length } });
|
|
292
|
+
await atomicWrite(join(destination, 'document.json'), document);
|
|
293
|
+
await atomicWrite(join(destination, 'converted.md'), blocks.map(b => blockMarkdown(b, assets)).join('\n\n') + '\n');
|
|
294
|
+
return document;
|
|
295
|
+
}
|
|
296
|
+
export function blockMarkdown(block, assets = [], assetPrefix = '') {
|
|
297
|
+
if (block.type === 'heading')
|
|
298
|
+
return '#'.repeat(Math.min(6, block.level)) + ' ' + block.text;
|
|
299
|
+
if (block.type === 'figure') {
|
|
300
|
+
const asset = assets.find(a => a.assetId === block.assetId);
|
|
301
|
+
return asset ? `})` : `> 图片未提取:${block.occurrenceId}`;
|
|
302
|
+
}
|
|
303
|
+
if (block.type === 'code')
|
|
304
|
+
return '```\n' + (block.text || '').replace(/```/g, '` ` `') + '\n```';
|
|
305
|
+
return block.markdown || block.text || '';
|
|
306
|
+
}
|
|
307
|
+
export async function legacyToDocx(file, executable, signal) {
|
|
308
|
+
if (!executable)
|
|
309
|
+
throw new Error('旧 .doc 需要另存为 .docx,或配置 libreOfficePath');
|
|
310
|
+
const dir = await mkdtemp(join(tmpdir(), 'dsh-kb-convert-'));
|
|
311
|
+
try {
|
|
312
|
+
await new Promise((yes, no) => {
|
|
313
|
+
const child = spawn(executable, ['-env:UserInstallation=file://' + dir.replace(/\\/g, '/'), '--headless', '--convert-to', 'docx', '--outdir', dir, file], { signal, stdio: ['ignore', 'ignore', 'pipe'] });
|
|
314
|
+
let error = '';
|
|
315
|
+
child.stderr.on('data', b => { error = (error + b).slice(-2000); });
|
|
316
|
+
child.on('error', no);
|
|
317
|
+
child.on('exit', code => code === 0 ? yes() : no(new Error(`Word 转换失败:${error}`)));
|
|
318
|
+
});
|
|
319
|
+
const { basename } = await import('node:path');
|
|
320
|
+
return await readFile(join(dir, basename(file).replace(/\.doc$/i, '.docx')));
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
await rm(dir, { recursive: true, force: true });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type blockSchema } from '../data.ts';
|
|
3
|
+
import type { Asset, Block, Box, Source, Warning } from '../data.ts';
|
|
4
|
+
export declare const spanSchema: z.ZodObject<{
|
|
5
|
+
id: z.ZodString;
|
|
6
|
+
text: z.ZodString;
|
|
7
|
+
bbox: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
8
|
+
readingBox: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
9
|
+
fontSize: z.ZodNumber;
|
|
10
|
+
fontName: z.ZodOptional<z.ZodString>;
|
|
11
|
+
mcid: z.ZodNullable<z.ZodString>;
|
|
12
|
+
artifact: z.ZodBoolean;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export type Span = z.infer<typeof spanSchema>;
|
|
15
|
+
export interface Structure {
|
|
16
|
+
type?: string | undefined;
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
role?: string | undefined;
|
|
19
|
+
children?: Structure[] | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare const structureSchema: z.ZodType<Structure>;
|
|
22
|
+
export declare const nativePageSchema: z.ZodObject<{
|
|
23
|
+
page: z.ZodNumber;
|
|
24
|
+
width: z.ZodNumber;
|
|
25
|
+
height: z.ZodNumber;
|
|
26
|
+
rotation: z.ZodNumber;
|
|
27
|
+
viewportTransform: z.ZodArray<z.ZodNumber>;
|
|
28
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
29
|
+
spans: z.ZodArray<z.ZodObject<{
|
|
30
|
+
id: z.ZodString;
|
|
31
|
+
text: z.ZodString;
|
|
32
|
+
bbox: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
33
|
+
readingBox: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
|
|
34
|
+
fontSize: z.ZodNumber;
|
|
35
|
+
fontName: z.ZodOptional<z.ZodString>;
|
|
36
|
+
mcid: z.ZodNullable<z.ZodString>;
|
|
37
|
+
artifact: z.ZodBoolean;
|
|
38
|
+
}, z.core.$strip>>;
|
|
39
|
+
tree: z.ZodNullable<z.ZodType<Structure, unknown, z.core.$ZodTypeInternals<Structure, unknown>>>;
|
|
40
|
+
drawings: z.ZodArray<z.ZodObject<{
|
|
41
|
+
bbox: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
42
|
+
kind: z.ZodEnum<{
|
|
43
|
+
path: "path";
|
|
44
|
+
image: "image";
|
|
45
|
+
}>;
|
|
46
|
+
shape: z.ZodNullable<z.ZodString>;
|
|
47
|
+
mcid: z.ZodNullable<z.ZodString>;
|
|
48
|
+
}, z.core.$strip>>;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
export type NativePage = z.infer<typeof nativePageSchema>;
|
|
51
|
+
export declare const pdfInfoSchema: z.ZodObject<{
|
|
52
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
53
|
+
pageCount: z.ZodNumber;
|
|
54
|
+
pageLabels: z.ZodNullable<z.ZodArray<z.ZodString>>;
|
|
55
|
+
outline: z.ZodArray<z.ZodObject<{
|
|
56
|
+
title: z.ZodString;
|
|
57
|
+
level: z.ZodNumber;
|
|
58
|
+
page: z.ZodNullable<z.ZodNumber>;
|
|
59
|
+
y: z.ZodNullable<z.ZodNumber>;
|
|
60
|
+
}, z.core.$strip>>;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
export type PdfInfo = z.infer<typeof pdfInfoSchema>;
|
|
63
|
+
export declare const renderSchema: z.ZodObject<{
|
|
64
|
+
data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
|
|
65
|
+
scale: z.ZodNumber;
|
|
66
|
+
bbox: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
|
|
67
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
68
|
+
}, z.core.$strip>;
|
|
69
|
+
export type RenderResult = z.infer<typeof renderSchema>;
|
|
70
|
+
export type PdfSource = Source & {
|
|
71
|
+
bbox: Box;
|
|
72
|
+
};
|
|
73
|
+
export interface LayoutBlock extends Block {
|
|
74
|
+
source: PdfSource;
|
|
75
|
+
captionFor?: LayoutBlock | undefined;
|
|
76
|
+
captionBlock?: LayoutBlock | undefined;
|
|
77
|
+
previewAsset?: Asset | undefined;
|
|
78
|
+
}
|
|
79
|
+
export declare const pdfBlock: (value: z.input<typeof blockSchema> & {
|
|
80
|
+
source: PdfSource;
|
|
81
|
+
}) => LayoutBlock;
|
|
82
|
+
export interface Layout {
|
|
83
|
+
blocks: LayoutBlock[];
|
|
84
|
+
warnings: Warning[];
|
|
85
|
+
excluded: {
|
|
86
|
+
kind: string;
|
|
87
|
+
text?: string | undefined;
|
|
88
|
+
bbox: Box;
|
|
89
|
+
}[];
|
|
90
|
+
ocr: boolean;
|
|
91
|
+
hasGarbled: boolean;
|
|
92
|
+
needsLayout: boolean;
|
|
93
|
+
navigation: boolean;
|
|
94
|
+
unexplained: Box[];
|
|
95
|
+
source: (bbox: Box, method?: string) => PdfSource;
|
|
96
|
+
rawText: string;
|
|
97
|
+
spanCount: number;
|
|
98
|
+
artifactCount: number;
|
|
99
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { bboxSchema, block as makeBlock } from "../data.js";
|
|
3
|
+
export const spanSchema = z.object({ id: z.string(), text: z.string(), bbox: bboxSchema, readingBox: bboxSchema.optional(), fontSize: z.number(), fontName: z.string().optional(), mcid: z.string().nullable(), artifact: z.boolean() });
|
|
4
|
+
export const structureSchema = z.lazy(() => z.object({ type: z.string().optional(), id: z.string().optional(), role: z.string().optional(), children: z.array(structureSchema).optional() }));
|
|
5
|
+
export const nativePageSchema = z.object({ page: z.number().int().positive(), width: z.number().positive(), height: z.number().positive(), rotation: z.number(), viewportTransform: z.array(z.number()).length(6),
|
|
6
|
+
warnings: z.array(z.string()), spans: z.array(spanSchema), tree: structureSchema.nullable(), drawings: z.array(z.object({ bbox: bboxSchema, kind: z.enum(['path', 'image']), shape: z.string().nullable(), mcid: z.string().nullable() })) });
|
|
7
|
+
export const pdfInfoSchema = z.object({ warnings: z.array(z.string()), pageCount: z.number().int().positive(), pageLabels: z.array(z.string()).nullable(), outline: z.array(z.object({ title: z.string(), level: z.number(), page: z.number().nullable(), y: z.number().nullable() })) });
|
|
8
|
+
export const renderSchema = z.object({ data: z.instanceof(Uint8Array), scale: z.number(), bbox: bboxSchema, warnings: z.array(z.string()) });
|
|
9
|
+
export const pdfBlock = (value) => ({ ...makeBlock(value), source: value.source });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Box, Cell } from '../data.ts';
|
|
2
|
+
import type { NativePage, PdfInfo, Span, Layout } from './pdf-data.ts';
|
|
3
|
+
export declare const PDF_PARSER_VERSION = "pdfjs-native-1";
|
|
4
|
+
export declare const normalize: (value: unknown) => string;
|
|
5
|
+
export declare const union: (boxes: Box[]) => Box | null;
|
|
6
|
+
export declare const intersects: (a: Box, b: Box) => boolean;
|
|
7
|
+
export declare function boundedBox(b: Box, page: Pick<NativePage, 'width' | 'height'>, pad?: number): Box;
|
|
8
|
+
export declare function lines(spans: Span[]): {
|
|
9
|
+
text: string;
|
|
10
|
+
spans: {
|
|
11
|
+
id: string;
|
|
12
|
+
text: string;
|
|
13
|
+
bbox: [number, number, number, number];
|
|
14
|
+
fontSize: number;
|
|
15
|
+
mcid: string | null;
|
|
16
|
+
artifact: boolean;
|
|
17
|
+
readingBox?: [number, number, number, number] | undefined;
|
|
18
|
+
fontName?: string | undefined;
|
|
19
|
+
}[];
|
|
20
|
+
bbox: [number, number, number, number];
|
|
21
|
+
}[];
|
|
22
|
+
export declare const textOf: (spans: Span[]) => string;
|
|
23
|
+
export declare function tableMarkdown(rows: Cell[][]): string;
|
|
24
|
+
export declare function nativeLayout(page: NativePage, info: PdfInfo): Layout;
|