mineru-layout-viewer 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/README.md +301 -0
- package/dist/index.mjs +444 -0
- package/dist/index.mjs.map +7 -0
- package/dist/mineru-layout-viewer.iife.js +2864 -0
- package/dist/mineru-layout-viewer.iife.js.map +7 -0
- package/package.json +53 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
// src/parse-blocks.ts
|
|
2
|
+
function extractSpanText(block) {
|
|
3
|
+
const lines = block.lines;
|
|
4
|
+
if (!lines) return block.text || "";
|
|
5
|
+
const texts = [];
|
|
6
|
+
for (const line of lines) {
|
|
7
|
+
const spans = line.spans;
|
|
8
|
+
if (spans) {
|
|
9
|
+
for (const span of spans) {
|
|
10
|
+
if (span.content) texts.push(String(span.content));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return texts.join(" ") || block.text || "";
|
|
15
|
+
}
|
|
16
|
+
function parseBlocks(jsonStr) {
|
|
17
|
+
const result = [];
|
|
18
|
+
try {
|
|
19
|
+
const data = JSON.parse(jsonStr);
|
|
20
|
+
if (data.pdf_info && Array.isArray(data.pdf_info)) {
|
|
21
|
+
const walkLayout = (items, pageIdx) => {
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
if (!item.bbox) continue;
|
|
24
|
+
const childBlocks = item.blocks;
|
|
25
|
+
if (childBlocks && childBlocks.length > 0) {
|
|
26
|
+
walkLayout(childBlocks, pageIdx);
|
|
27
|
+
} else {
|
|
28
|
+
result.push({
|
|
29
|
+
page_idx: pageIdx,
|
|
30
|
+
bbox: item.bbox,
|
|
31
|
+
text: extractSpanText(item) || void 0,
|
|
32
|
+
type: item.type
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
for (let i = 0; i < data.pdf_info.length; i++) {
|
|
38
|
+
const page = data.pdf_info[i];
|
|
39
|
+
const blocks = page.preproc_blocks || page.para_blocks || [];
|
|
40
|
+
walkLayout(blocks, i);
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
if (!Array.isArray(data)) return result;
|
|
45
|
+
const walk = (items) => {
|
|
46
|
+
for (const item of items) {
|
|
47
|
+
const idx = item.page_idx ?? item.page_index;
|
|
48
|
+
if (idx !== void 0 && item.bbox) {
|
|
49
|
+
result.push({
|
|
50
|
+
page_idx: idx,
|
|
51
|
+
bbox: item.bbox,
|
|
52
|
+
text: item.text || void 0,
|
|
53
|
+
type: item.category || item.type
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(item.children)) walk(item.children);
|
|
57
|
+
if (Array.isArray(item.blocks)) walk(item.blocks);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
walk(data);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/match-markdown.ts
|
|
67
|
+
function normalize(s) {
|
|
68
|
+
return s.replace(/[#*\s\n\r\t`~|>\\[\]()]+/g, " ").replace(/\s{2,}/g, " ").trim().toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
function lcsSimilarity(a, b) {
|
|
71
|
+
const shorter = a.length < b.length ? a : b;
|
|
72
|
+
const longer = a.length < b.length ? b : a;
|
|
73
|
+
if (shorter.length === 0) return 0;
|
|
74
|
+
let maxLen = 0;
|
|
75
|
+
const window2 = Math.min(shorter.length, 30);
|
|
76
|
+
for (let i = 0; i < shorter.length; i++) {
|
|
77
|
+
if (shorter.length - i <= maxLen) break;
|
|
78
|
+
for (let len = window2; len > maxLen; len--) {
|
|
79
|
+
const sub = shorter.substring(i, i + len);
|
|
80
|
+
if (sub.length < 4) continue;
|
|
81
|
+
if (longer.includes(sub)) {
|
|
82
|
+
maxLen = sub.length;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return maxLen / Math.max(shorter.length, 1);
|
|
88
|
+
}
|
|
89
|
+
function matchMarkdownToPdf(markdown, blocks) {
|
|
90
|
+
const paragraphs = markdown.split(/\n+/).filter((p) => p.trim());
|
|
91
|
+
const textBlocks = blocks.filter(
|
|
92
|
+
(b) => b.text && b.text.trim().length > 1
|
|
93
|
+
);
|
|
94
|
+
if (textBlocks.length === 0) {
|
|
95
|
+
return paragraphs.map((p) => ({ text: p, page: 1, bbox: null }));
|
|
96
|
+
}
|
|
97
|
+
return paragraphs.map((para) => {
|
|
98
|
+
const norm = normalize(para);
|
|
99
|
+
if (norm.length < 4) {
|
|
100
|
+
return { text: para, page: textBlocks[0].page_idx + 1, bbox: textBlocks[0].bbox };
|
|
101
|
+
}
|
|
102
|
+
let best = null;
|
|
103
|
+
let bestScore = 0;
|
|
104
|
+
const topBlocks = textBlocks.filter(
|
|
105
|
+
(b) => b.type !== "table-body" && b.type !== "table-row"
|
|
106
|
+
);
|
|
107
|
+
for (const b of topBlocks) {
|
|
108
|
+
const s = lcsSimilarity(norm, normalize(b.text));
|
|
109
|
+
if (s > bestScore) {
|
|
110
|
+
bestScore = s;
|
|
111
|
+
best = b;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (bestScore < 0.2) {
|
|
115
|
+
for (const b of textBlocks) {
|
|
116
|
+
const s = lcsSimilarity(norm, normalize(b.text));
|
|
117
|
+
if (s > bestScore) {
|
|
118
|
+
bestScore = s;
|
|
119
|
+
best = b;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!best || bestScore < 0.1) {
|
|
124
|
+
const ratio = paragraphs.indexOf(para) / Math.max(paragraphs.length, 1);
|
|
125
|
+
const estPage = Math.floor(
|
|
126
|
+
ratio * (blocks.length > 0 ? Math.max(...blocks.map((b) => b.page_idx)) + 1 : 1)
|
|
127
|
+
);
|
|
128
|
+
return { text: para, page: estPage + 1, bbox: null };
|
|
129
|
+
}
|
|
130
|
+
return { text: para, page: best.page_idx + 1, bbox: best.bbox };
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/mineru-viewer.ts
|
|
135
|
+
var RENDER_SCALE = 2;
|
|
136
|
+
var STYLES = `
|
|
137
|
+
:host { display: flex; flex-direction: column; height: 100%; font-family: system-ui, sans-serif; color-scheme: light dark; }
|
|
138
|
+
.toolbar { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-bottom: 1px solid #e5e5e5; font-size: 12px; color: #888; flex-shrink: 0; flex-wrap: wrap; }
|
|
139
|
+
.toolbar .sep { color: #ddd; }
|
|
140
|
+
.toolbar .ok { color: #16a34a; }
|
|
141
|
+
.toolbar .warn { color: #f59e0b; }
|
|
142
|
+
@media (prefers-color-scheme: dark) {
|
|
143
|
+
.toolbar { border-color: #333; }
|
|
144
|
+
.toolbar .sep { color: #555; }
|
|
145
|
+
}
|
|
146
|
+
.split { flex: 1; display: grid; grid-template-columns: 1fr 1fr; min-height: 0; overflow: hidden; }
|
|
147
|
+
.pane { overflow: auto; padding: 10px; }
|
|
148
|
+
.pane-left { border-right: 1px solid #e5e5e5; }
|
|
149
|
+
.pane-right { background: #f9f9f9; }
|
|
150
|
+
@media (prefers-color-scheme: dark) {
|
|
151
|
+
.pane-left { border-color: #333; }
|
|
152
|
+
.pane-right { background: #1e1e1e; }
|
|
153
|
+
}
|
|
154
|
+
.pdf-page { position: relative; margin: 0 auto 12px; border: 1px solid #e5e5e5; border-radius: 4px; overflow: hidden; }
|
|
155
|
+
.pdf-page img { display: block; width: 100%; }
|
|
156
|
+
.pdf-page .page-num { position: absolute; bottom: 2px; right: 4px; font-size: 9px; color: #999; background: rgba(255,255,255,.85); padding: 1px 4px; border-radius: 3px; }
|
|
157
|
+
@media (prefers-color-scheme: dark) { .pdf-page { border-color: #333; } .pdf-page .page-num { background: rgba(0,0,0,.7); } }
|
|
158
|
+
.block-overlay { position: absolute; border: 1px solid transparent; cursor: pointer; transition: all .15s; }
|
|
159
|
+
.block-overlay:hover { border-color: #f59e0b; background: rgba(245,158,11,.12); }
|
|
160
|
+
.block-overlay.active { border-color: #3b82f6 !important; background: rgba(59,130,246,.2) !important; z-index: 10; box-shadow: 0 0 0 1px #3b82f6; }
|
|
161
|
+
.md-line { display: block; cursor: pointer; padding: 2px 8px; border-radius: 4px; border-left: 2px solid transparent; font-size: 13px; line-height: 1.5; font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; white-space: pre-wrap; word-break: break-all; }
|
|
162
|
+
.md-line.match { border-left-color: rgba(245,158,11,.4); }
|
|
163
|
+
.md-line.match:hover { background: rgba(245,158,11,.08); }
|
|
164
|
+
.md-line.no-match { color: #999; opacity: .6; }
|
|
165
|
+
.md-line.active { background: rgba(59,130,246,.1); border-left-color: #3b82f6; box-shadow: inset 0 0 0 1px rgba(59,130,246,.3); }
|
|
166
|
+
@media (prefers-color-scheme: dark) { .md-line.active { background: rgba(59,130,246,.15); } }
|
|
167
|
+
.md-line .badge { font-size: 10px; color: #999; margin-left: 6px; }
|
|
168
|
+
`;
|
|
169
|
+
var MineruLayoutViewer = class extends HTMLElement {
|
|
170
|
+
blocks = [];
|
|
171
|
+
sections = [];
|
|
172
|
+
pages = [];
|
|
173
|
+
activeIdx = null;
|
|
174
|
+
pdfUrl = null;
|
|
175
|
+
layoutData = null;
|
|
176
|
+
markdownText = null;
|
|
177
|
+
static observedAttributes = ["pdf", "layout", "markdown"];
|
|
178
|
+
constructor() {
|
|
179
|
+
super();
|
|
180
|
+
this.attachShadow({ mode: "open" });
|
|
181
|
+
}
|
|
182
|
+
connectedCallback() {
|
|
183
|
+
this.render();
|
|
184
|
+
this.setupResize();
|
|
185
|
+
}
|
|
186
|
+
attributeChangedCallback(name, _old, newVal) {
|
|
187
|
+
if (name === "pdf" && newVal) {
|
|
188
|
+
this.pdfUrl = newVal;
|
|
189
|
+
this.loadPdf(newVal);
|
|
190
|
+
}
|
|
191
|
+
if (name === "layout" && newVal) {
|
|
192
|
+
this.loadLayout(newVal);
|
|
193
|
+
}
|
|
194
|
+
if (name === "markdown" && newVal) {
|
|
195
|
+
this.markdownText = newVal;
|
|
196
|
+
this.rebuild();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// ── Public API ──
|
|
200
|
+
set pdf(value) {
|
|
201
|
+
this.setAttribute("pdf", value);
|
|
202
|
+
}
|
|
203
|
+
get pdf() {
|
|
204
|
+
return this.getAttribute("pdf") || "";
|
|
205
|
+
}
|
|
206
|
+
set layout(value) {
|
|
207
|
+
this.setAttribute("layout", value);
|
|
208
|
+
}
|
|
209
|
+
get layout() {
|
|
210
|
+
return this.getAttribute("layout") || "";
|
|
211
|
+
}
|
|
212
|
+
set markdown(value) {
|
|
213
|
+
this.setAttribute("markdown", value);
|
|
214
|
+
}
|
|
215
|
+
get markdown() {
|
|
216
|
+
return this.getAttribute("markdown") || "";
|
|
217
|
+
}
|
|
218
|
+
/** Programmatic API: load layout JSON directly */
|
|
219
|
+
async loadLayoutFromJson(data) {
|
|
220
|
+
const jsonStr = typeof data === "string" ? data : JSON.stringify(data);
|
|
221
|
+
this.layoutData = jsonStr;
|
|
222
|
+
if (this.pdfUrl) this.rebuild();
|
|
223
|
+
}
|
|
224
|
+
/** Programmatic API: load markdown text directly */
|
|
225
|
+
async loadMarkdown(text) {
|
|
226
|
+
this.markdownText = text;
|
|
227
|
+
if (this.layoutData) this.rebuild();
|
|
228
|
+
}
|
|
229
|
+
/** Programmatic API: load PDF + layout from a MinerU zip Blob */
|
|
230
|
+
async loadZip(zipBlob) {
|
|
231
|
+
const JSZip = window.JSZip || await import("jszip").then((m) => m.default);
|
|
232
|
+
const zip = await JSZip.loadAsync(zipBlob);
|
|
233
|
+
let jsonStr = "";
|
|
234
|
+
for (const name of ["layout.json", "middle.json"]) {
|
|
235
|
+
const f = zip.file(name);
|
|
236
|
+
if (f) {
|
|
237
|
+
jsonStr = await f.async("text");
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (!jsonStr) {
|
|
242
|
+
for (const name of Object.keys(zip.files)) {
|
|
243
|
+
if (name.endsWith("_layout.json") || name.endsWith("_middle.json")) {
|
|
244
|
+
jsonStr = await zip.file(name).async("text");
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (!jsonStr) {
|
|
250
|
+
for (const name of Object.keys(zip.files)) {
|
|
251
|
+
if (name.endsWith("_content_list.json")) {
|
|
252
|
+
jsonStr = await zip.file(name).async("text");
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const mdFile = zip.file("full.md");
|
|
258
|
+
if (mdFile) this.markdownText = await mdFile.async("text");
|
|
259
|
+
this.layoutData = jsonStr;
|
|
260
|
+
for (const name of Object.keys(zip.files)) {
|
|
261
|
+
if (name.endsWith("_origin.pdf")) {
|
|
262
|
+
const blob = await zip.file(name).async("blob");
|
|
263
|
+
this.pdfUrl = URL.createObjectURL(blob);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (this.layoutData) this.rebuild();
|
|
268
|
+
else throw new Error("zip \u7F3A\u5C11 layout.json \u6216 middle.json");
|
|
269
|
+
}
|
|
270
|
+
// ── Internal ──
|
|
271
|
+
render() {
|
|
272
|
+
if (!this.shadowRoot) return;
|
|
273
|
+
this.shadowRoot.innerHTML = `<style>${STYLES}</style>
|
|
274
|
+
<div class="toolbar">
|
|
275
|
+
<span id="stat"></span><span class="flex-1" style="flex:1"></span>
|
|
276
|
+
</div>
|
|
277
|
+
<div class="split">
|
|
278
|
+
<div class="pane pane-left" id="pdfPane"><slot name="loading">\u52A0\u8F7D PDF + layout.json \u4EE5\u5F00\u59CB</slot></div>
|
|
279
|
+
<div class="pane pane-right" id="mdPane"></div>
|
|
280
|
+
</div>`;
|
|
281
|
+
}
|
|
282
|
+
setupResize() {
|
|
283
|
+
const ro = new ResizeObserver(() => this.buildPdfOverlays());
|
|
284
|
+
const pane = this.shadowRoot?.getElementById("pdfPane");
|
|
285
|
+
if (pane) ro.observe(pane);
|
|
286
|
+
}
|
|
287
|
+
async loadPdf(url) {
|
|
288
|
+
this.pdfUrl = url;
|
|
289
|
+
if (this.layoutData || this.markdownText) await this.rebuild();
|
|
290
|
+
}
|
|
291
|
+
async loadLayout(url) {
|
|
292
|
+
const res = await fetch(url);
|
|
293
|
+
this.layoutData = await res.text();
|
|
294
|
+
if (this.pdfUrl) await this.rebuild();
|
|
295
|
+
}
|
|
296
|
+
async rebuild() {
|
|
297
|
+
if (!this.layoutData) return;
|
|
298
|
+
this.blocks = parseBlocks(this.layoutData);
|
|
299
|
+
const md = this.markdownText || this.blocks.map((b) => b.text || "").filter(Boolean).join("\n");
|
|
300
|
+
this.sections = matchMarkdownToPdf(md, this.blocks);
|
|
301
|
+
if (this.pdfUrl) await this.renderPdfPages();
|
|
302
|
+
this.buildUI();
|
|
303
|
+
}
|
|
304
|
+
async renderPdfPages() {
|
|
305
|
+
if (!this.pdfUrl) return;
|
|
306
|
+
const pdf = await pdfjsLib.getDocument(this.pdfUrl).promise;
|
|
307
|
+
this.pages = [];
|
|
308
|
+
for (let i = 1; i <= pdf.numPages; i++) {
|
|
309
|
+
const page = await pdf.getPage(i);
|
|
310
|
+
const vp = page.getViewport({ scale: RENDER_SCALE });
|
|
311
|
+
const cvs = document.createElement("canvas");
|
|
312
|
+
cvs.width = vp.width;
|
|
313
|
+
cvs.height = vp.height;
|
|
314
|
+
await page.render({ canvasContext: cvs.getContext("2d"), viewport: vp }).promise;
|
|
315
|
+
this.pages.push({ p: i, w: vp.width, h: vp.height, src: cvs.toDataURL() });
|
|
316
|
+
page.cleanup();
|
|
317
|
+
}
|
|
318
|
+
pdf.destroy();
|
|
319
|
+
}
|
|
320
|
+
buildUI() {
|
|
321
|
+
const shadow = this.shadowRoot;
|
|
322
|
+
const matched = this.sections.filter((s) => s.bbox).length;
|
|
323
|
+
shadow.getElementById("stat").innerHTML = `${this.pages.length} \u9875 | ${this.sections.length} \u884C | <span class="${matched > 0 ? "ok" : "warn"}">\u5339\u914D ${matched}</span> | ${this.blocks.length} \u5757`;
|
|
324
|
+
this.buildPdfOverlays();
|
|
325
|
+
this.buildMarkdown();
|
|
326
|
+
}
|
|
327
|
+
buildPdfOverlays() {
|
|
328
|
+
const pane = this.shadowRoot.getElementById("pdfPane");
|
|
329
|
+
pane.innerHTML = "";
|
|
330
|
+
const containerW = pane.clientWidth - 20;
|
|
331
|
+
if (containerW <= 0 || this.pages.length === 0) return;
|
|
332
|
+
for (const rp of this.pages) {
|
|
333
|
+
const cssW = containerW;
|
|
334
|
+
const cssH = rp.h * (containerW / rp.w);
|
|
335
|
+
const pageBlocks = this.blocks.filter((b) => b.page_idx === rp.p - 1);
|
|
336
|
+
const pageScale = containerW / rp.w;
|
|
337
|
+
const s = RENDER_SCALE * pageScale;
|
|
338
|
+
const wrapper = document.createElement("div");
|
|
339
|
+
wrapper.className = "pdf-page";
|
|
340
|
+
wrapper.style.width = cssW + "px";
|
|
341
|
+
wrapper.style.height = cssH + "px";
|
|
342
|
+
wrapper.dataset.page = String(rp.p);
|
|
343
|
+
const img = document.createElement("img");
|
|
344
|
+
img.src = rp.src;
|
|
345
|
+
wrapper.appendChild(img);
|
|
346
|
+
const label = document.createElement("span");
|
|
347
|
+
label.className = "page-num";
|
|
348
|
+
label.textContent = String(rp.p);
|
|
349
|
+
wrapper.appendChild(label);
|
|
350
|
+
for (const b of pageBlocks) {
|
|
351
|
+
const [x0, y0, x1, y1] = b.bbox;
|
|
352
|
+
const ov = document.createElement("div");
|
|
353
|
+
ov.className = "block-overlay";
|
|
354
|
+
ov.style.left = x0 * s + "px";
|
|
355
|
+
ov.style.top = y0 * s + "px";
|
|
356
|
+
ov.style.width = Math.max((x1 - x0) * s, 2) + "px";
|
|
357
|
+
ov.style.height = Math.max((y1 - y0) * s, 2) + "px";
|
|
358
|
+
ov.title = (b.text || "").slice(0, 120);
|
|
359
|
+
ov.addEventListener("click", () => this.onBlockClick(b, ov));
|
|
360
|
+
wrapper.appendChild(ov);
|
|
361
|
+
}
|
|
362
|
+
pane.appendChild(wrapper);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
buildMarkdown() {
|
|
366
|
+
const pane = this.shadowRoot.getElementById("mdPane");
|
|
367
|
+
pane.innerHTML = "";
|
|
368
|
+
for (let i = 0; i < this.sections.length; i++) {
|
|
369
|
+
const sec = this.sections[i];
|
|
370
|
+
const el = document.createElement("span");
|
|
371
|
+
el.className = "md-line" + (sec.bbox ? " match" : " no-match");
|
|
372
|
+
el.dataset.idx = String(i);
|
|
373
|
+
el.textContent = sec.text;
|
|
374
|
+
if (sec.bbox) {
|
|
375
|
+
const b = document.createElement("span");
|
|
376
|
+
b.className = "badge";
|
|
377
|
+
b.textContent = `p${sec.page}`;
|
|
378
|
+
el.appendChild(b);
|
|
379
|
+
}
|
|
380
|
+
el.addEventListener("click", () => this.onMdClick(sec, i, el));
|
|
381
|
+
pane.appendChild(el);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
onMdClick(sec, idx, el) {
|
|
385
|
+
const shadow = this.shadowRoot;
|
|
386
|
+
shadow.querySelectorAll(".md-line.active").forEach((e) => e.classList.remove("active"));
|
|
387
|
+
shadow.querySelectorAll(".block-overlay.active").forEach((e) => e.classList.remove("active"));
|
|
388
|
+
el.classList.add("active");
|
|
389
|
+
this.activeIdx = idx;
|
|
390
|
+
if (sec.bbox) {
|
|
391
|
+
const pageEl = shadow.querySelector(`.pdf-page[data-page="${sec.page}"]`);
|
|
392
|
+
if (pageEl) pageEl.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
393
|
+
const overlays = shadow.querySelectorAll(`.pdf-page[data-page="${sec.page}"] .block-overlay`);
|
|
394
|
+
const page = this.pages[sec.page - 1];
|
|
395
|
+
if (page) {
|
|
396
|
+
const pw = shadow.getElementById("pdfPane").clientWidth - 20;
|
|
397
|
+
const s = RENDER_SCALE * (pw / page.w);
|
|
398
|
+
overlays.forEach((ov) => {
|
|
399
|
+
const el2 = ov;
|
|
400
|
+
if (Math.abs(parseFloat(el2.style.left) - sec.bbox[0] * s) < 4) {
|
|
401
|
+
el2.classList.add("active");
|
|
402
|
+
el2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
onBlockClick(block, ov) {
|
|
409
|
+
const shadow = this.shadowRoot;
|
|
410
|
+
shadow.querySelectorAll(".md-line.active").forEach((e) => e.classList.remove("active"));
|
|
411
|
+
shadow.querySelectorAll(".block-overlay.active").forEach((e) => e.classList.remove("active"));
|
|
412
|
+
ov.classList.add("active");
|
|
413
|
+
const blockNorm = normalize(block.text || "");
|
|
414
|
+
let bestIdx = -1, bestSim = 0;
|
|
415
|
+
for (let i = 0; i < this.sections.length; i++) {
|
|
416
|
+
if (!this.sections[i].bbox) continue;
|
|
417
|
+
const sim = lcsSimilarity(blockNorm, normalize(this.sections[i].text));
|
|
418
|
+
if (sim > bestSim && sim > 0.05) {
|
|
419
|
+
bestSim = sim;
|
|
420
|
+
bestIdx = i;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (bestIdx >= 0) {
|
|
424
|
+
this.activeIdx = bestIdx;
|
|
425
|
+
const el = shadow.querySelector(`[data-idx="${bestIdx}"]`);
|
|
426
|
+
if (el) {
|
|
427
|
+
el.classList.add("active");
|
|
428
|
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
if (typeof customElements !== "undefined" && !customElements.get("mineru-layout-viewer")) {
|
|
434
|
+
customElements.define("mineru-layout-viewer", MineruLayoutViewer);
|
|
435
|
+
}
|
|
436
|
+
export {
|
|
437
|
+
MineruLayoutViewer,
|
|
438
|
+
extractSpanText,
|
|
439
|
+
lcsSimilarity,
|
|
440
|
+
matchMarkdownToPdf,
|
|
441
|
+
normalize,
|
|
442
|
+
parseBlocks
|
|
443
|
+
};
|
|
444
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/parse-blocks.ts", "../src/match-markdown.ts", "../src/mineru-viewer.ts"],
|
|
4
|
+
"sourcesContent": ["// \u2500\u2500 Block types \u2500\u2500\n\nexport interface PdfBlock {\n page_idx: number\n bbox: [number, number, number, number]\n text?: string\n type?: string\n}\n\nexport interface MdSection {\n text: string\n page: number\n bbox: [number, number, number, number] | null\n}\n\n// \u2500\u2500 Text extraction from layout.json / middle.json span structure \u2500\u2500\n\nexport function extractSpanText(block: Record<string, unknown>): string {\n const lines = block.lines as Array<Record<string, unknown>> | undefined\n if (!lines) return (block.text as string) || ''\n const texts: string[] = []\n for (const line of lines) {\n const spans = line.spans as Array<Record<string, unknown>> | undefined\n if (spans) {\n for (const span of spans) {\n if (span.content) texts.push(String(span.content))\n }\n }\n }\n return texts.join(' ') || (block.text as string) || ''\n}\n\n// \u2500\u2500 Block parser (supports layout.json, middle.json, content_list.json) \u2500\u2500\n\nexport function parseBlocks(jsonStr: string): PdfBlock[] {\n const result: PdfBlock[] = []\n try {\n const data = JSON.parse(jsonStr)\n\n // layout.json / middle.json: { pdf_info: [{ preproc_blocks, ... }, ...] }\n if (data.pdf_info && Array.isArray(data.pdf_info)) {\n const walkLayout = (items: Record<string, unknown>[], pageIdx: number) => {\n for (const item of items) {\n if (!item.bbox) continue\n const childBlocks = item.blocks as Record<string, unknown>[] | undefined\n if (childBlocks && childBlocks.length > 0) {\n walkLayout(childBlocks, pageIdx)\n } else {\n result.push({\n page_idx: pageIdx,\n bbox: item.bbox as [number, number, number, number],\n text: extractSpanText(item) || undefined,\n type: item.type as string | undefined,\n })\n }\n }\n }\n for (let i = 0; i < data.pdf_info.length; i++) {\n const page = data.pdf_info[i] as Record<string, unknown>\n const blocks = (page.preproc_blocks || page.para_blocks || []) as Record<string, unknown>[]\n walkLayout(blocks, i)\n }\n return result\n }\n\n // content_list.json: [{ page_idx, bbox, text, type, children?, blocks? }, ...]\n if (!Array.isArray(data)) return result\n const walk = (items: Record<string, unknown>[]) => {\n for (const item of items) {\n const idx = (item.page_idx ?? item.page_index) as number | undefined\n if (idx !== undefined && item.bbox) {\n result.push({\n page_idx: idx,\n bbox: item.bbox as [number, number, number, number],\n text: (item.text as string) || undefined,\n type: (item.category || item.type) as string | undefined,\n })\n }\n if (Array.isArray(item.children)) walk(item.children as Record<string, unknown>[])\n if (Array.isArray(item.blocks)) walk(item.blocks as Record<string, unknown>[])\n }\n }\n walk(data)\n } catch { /* ignore parse errors */ }\n return result\n}\n", "import type { PdfBlock, MdSection } from './parse-blocks.js'\n\n// \u2500\u2500 Text normalization \u2500\u2500\n\nexport function normalize(s: string): string {\n return s\n .replace(/[#*\\s\\n\\r\\t`~|>\\\\[\\]()]+/g, ' ')\n .replace(/\\s{2,}/g, ' ')\n .trim()\n .toLowerCase()\n}\n\n// \u2500\u2500 LCS-based similarity (longest common substring) \u2500\u2500\n\nexport function lcsSimilarity(a: string, b: string): number {\n const shorter = a.length < b.length ? a : b\n const longer = a.length < b.length ? b : a\n if (shorter.length === 0) return 0\n\n let maxLen = 0\n const window = Math.min(shorter.length, 30)\n for (let i = 0; i < shorter.length; i++) {\n if (shorter.length - i <= maxLen) break\n for (let len = window; len > maxLen; len--) {\n const sub = shorter.substring(i, i + len)\n if (sub.length < 4) continue\n if (longer.includes(sub)) {\n maxLen = sub.length\n break\n }\n }\n }\n return maxLen / Math.max(shorter.length, 1)\n}\n\n// \u2500\u2500 Match markdown paragraphs to PDF blocks \u2500\u2500\n\nexport function matchMarkdownToPdf(\n markdown: string,\n blocks: PdfBlock[],\n): MdSection[] {\n // Split by line breaks \u2014 layout.json / middle.json blocks are line-level\n const paragraphs = markdown.split(/\\n+/).filter((p) => p.trim())\n const textBlocks = blocks.filter(\n (b) => b.text && b.text.trim().length > 1,\n )\n\n if (textBlocks.length === 0) {\n return paragraphs.map((p) => ({ text: p, page: 1, bbox: null }))\n }\n\n return paragraphs.map((para) => {\n const norm = normalize(para)\n if (norm.length < 4) {\n return { text: para, page: textBlocks[0].page_idx + 1, bbox: textBlocks[0].bbox }\n }\n\n let best: PdfBlock | null = null\n let bestScore = 0\n\n // First pass: skip table internals\n const topBlocks = textBlocks.filter(\n (b) => b.type !== 'table-body' && b.type !== 'table-row',\n )\n for (const b of topBlocks) {\n const s = lcsSimilarity(norm, normalize(b.text!))\n if (s > bestScore) {\n bestScore = s\n best = b\n }\n }\n\n // Fallback: include table blocks\n if (bestScore < 0.2) {\n for (const b of textBlocks) {\n const s = lcsSimilarity(norm, normalize(b.text!))\n if (s > bestScore) {\n bestScore = s\n best = b\n }\n }\n }\n\n // If nothing matches well, estimate by paragraph position\n if (!best || bestScore < 0.1) {\n const ratio =\n paragraphs.indexOf(para) / Math.max(paragraphs.length, 1)\n const estPage = Math.floor(\n ratio *\n (blocks.length > 0\n ? Math.max(...blocks.map((b) => b.page_idx)) + 1\n : 1),\n )\n return { text: para, page: estPage + 1, bbox: null }\n }\n\n return { text: para, page: best.page_idx + 1, bbox: best.bbox }\n })\n}\n", "import { parseBlocks } from './parse-blocks.js'\nimport { matchMarkdownToPdf } from './match-markdown.js'\nimport type { PdfBlock, MdSection } from './parse-blocks.js'\nimport { normalize, lcsSimilarity } from './match-markdown.js'\n\n// We load pdf.js worker from CDN \u2014 consumer can override via window.PDFJS_WORKER_SRC\ndeclare const pdfjsLib: typeof import('pdfjs-dist')\n\nconst RENDER_SCALE = 2.0\n\nconst STYLES = `\n:host { display: flex; flex-direction: column; height: 100%; font-family: system-ui, sans-serif; color-scheme: light dark; }\n.toolbar { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-bottom: 1px solid #e5e5e5; font-size: 12px; color: #888; flex-shrink: 0; flex-wrap: wrap; }\n.toolbar .sep { color: #ddd; }\n.toolbar .ok { color: #16a34a; }\n.toolbar .warn { color: #f59e0b; }\n@media (prefers-color-scheme: dark) {\n .toolbar { border-color: #333; }\n .toolbar .sep { color: #555; }\n}\n.split { flex: 1; display: grid; grid-template-columns: 1fr 1fr; min-height: 0; overflow: hidden; }\n.pane { overflow: auto; padding: 10px; }\n.pane-left { border-right: 1px solid #e5e5e5; }\n.pane-right { background: #f9f9f9; }\n@media (prefers-color-scheme: dark) {\n .pane-left { border-color: #333; }\n .pane-right { background: #1e1e1e; }\n}\n.pdf-page { position: relative; margin: 0 auto 12px; border: 1px solid #e5e5e5; border-radius: 4px; overflow: hidden; }\n.pdf-page img { display: block; width: 100%; }\n.pdf-page .page-num { position: absolute; bottom: 2px; right: 4px; font-size: 9px; color: #999; background: rgba(255,255,255,.85); padding: 1px 4px; border-radius: 3px; }\n@media (prefers-color-scheme: dark) { .pdf-page { border-color: #333; } .pdf-page .page-num { background: rgba(0,0,0,.7); } }\n.block-overlay { position: absolute; border: 1px solid transparent; cursor: pointer; transition: all .15s; }\n.block-overlay:hover { border-color: #f59e0b; background: rgba(245,158,11,.12); }\n.block-overlay.active { border-color: #3b82f6 !important; background: rgba(59,130,246,.2) !important; z-index: 10; box-shadow: 0 0 0 1px #3b82f6; }\n.md-line { display: block; cursor: pointer; padding: 2px 8px; border-radius: 4px; border-left: 2px solid transparent; font-size: 13px; line-height: 1.5; font-family: 'SF Mono', 'Cascadia Code', Consolas, monospace; white-space: pre-wrap; word-break: break-all; }\n.md-line.match { border-left-color: rgba(245,158,11,.4); }\n.md-line.match:hover { background: rgba(245,158,11,.08); }\n.md-line.no-match { color: #999; opacity: .6; }\n.md-line.active { background: rgba(59,130,246,.1); border-left-color: #3b82f6; box-shadow: inset 0 0 0 1px rgba(59,130,246,.3); }\n@media (prefers-color-scheme: dark) { .md-line.active { background: rgba(59,130,246,.15); } }\n.md-line .badge { font-size: 10px; color: #999; margin-left: 6px; }\n`\n\nexport class MineruLayoutViewer extends HTMLElement {\n private blocks: PdfBlock[] = []\n private sections: MdSection[] = []\n private pages: { p: number; w: number; h: number; src: string }[] = []\n private activeIdx: number | null = null\n private pdfUrl: string | null = null\n private layoutData: string | null = null\n private markdownText: string | null = null\n\n static observedAttributes = ['pdf', 'layout', 'markdown']\n\n constructor() {\n super()\n this.attachShadow({ mode: 'open' })\n }\n\n connectedCallback() {\n this.render()\n this.setupResize()\n }\n\n attributeChangedCallback(name: string, _old: string | null, newVal: string | null) {\n if (name === 'pdf' && newVal) {\n this.pdfUrl = newVal\n this.loadPdf(newVal)\n }\n if (name === 'layout' && newVal) {\n this.loadLayout(newVal)\n }\n if (name === 'markdown' && newVal) {\n this.markdownText = newVal\n this.rebuild()\n }\n }\n\n // \u2500\u2500 Public API \u2500\u2500\n\n set pdf(value: string) { this.setAttribute('pdf', value) }\n get pdf(): string { return this.getAttribute('pdf') || '' }\n\n set layout(value: string) { this.setAttribute('layout', value) }\n get layout(): string { return this.getAttribute('layout') || '' }\n\n set markdown(value: string) { this.setAttribute('markdown', value) }\n get markdown(): string { return this.getAttribute('markdown') || '' }\n\n /** Programmatic API: load layout JSON directly */\n async loadLayoutFromJson(data: Record<string, unknown> | string) {\n const jsonStr = typeof data === 'string' ? data : JSON.stringify(data)\n this.layoutData = jsonStr\n if (this.pdfUrl) this.rebuild()\n }\n\n /** Programmatic API: load markdown text directly */\n async loadMarkdown(text: string) {\n this.markdownText = text\n if (this.layoutData) this.rebuild()\n }\n\n /** Programmatic API: load PDF + layout from a MinerU zip Blob */\n async loadZip(zipBlob: Blob) {\n const JSZip = (window as any).JSZip || await import('jszip').then(m => m.default)\n const zip = await JSZip.loadAsync(zipBlob)\n let jsonStr = ''\n\n for (const name of ['layout.json', 'middle.json']) {\n const f = zip.file(name); if (f) { jsonStr = await f.async('text'); break }\n }\n if (!jsonStr) {\n for (const name of Object.keys(zip.files)) {\n if (name.endsWith('_layout.json') || name.endsWith('_middle.json')) {\n jsonStr = await zip.file(name)!.async('text'); break\n }\n }\n }\n if (!jsonStr) {\n for (const name of Object.keys(zip.files)) {\n if (name.endsWith('_content_list.json')) {\n jsonStr = await zip.file(name)!.async('text'); break\n }\n }\n }\n\n const mdFile = zip.file('full.md')\n if (mdFile) this.markdownText = await mdFile.async('text')\n\n this.layoutData = jsonStr\n\n // Find PDF\n for (const name of Object.keys(zip.files)) {\n if (name.endsWith('_origin.pdf')) {\n const blob = await zip.file(name)!.async('blob')\n this.pdfUrl = URL.createObjectURL(blob)\n break\n }\n }\n\n if (this.layoutData) this.rebuild()\n else throw new Error('zip \u7F3A\u5C11 layout.json \u6216 middle.json')\n }\n\n // \u2500\u2500 Internal \u2500\u2500\n\n private render() {\n if (!this.shadowRoot) return\n this.shadowRoot.innerHTML = `<style>${STYLES}</style>\n <div class=\"toolbar\">\n <span id=\"stat\"></span><span class=\"flex-1\" style=\"flex:1\"></span>\n </div>\n <div class=\"split\">\n <div class=\"pane pane-left\" id=\"pdfPane\"><slot name=\"loading\">\u52A0\u8F7D PDF + layout.json \u4EE5\u5F00\u59CB</slot></div>\n <div class=\"pane pane-right\" id=\"mdPane\"></div>\n </div>`\n }\n\n private setupResize() {\n const ro = new ResizeObserver(() => this.buildPdfOverlays())\n const pane = this.shadowRoot?.getElementById('pdfPane')\n if (pane) ro.observe(pane)\n }\n\n private async loadPdf(url: string) {\n this.pdfUrl = url\n if (this.layoutData || this.markdownText) await this.rebuild()\n }\n\n private async loadLayout(url: string) {\n const res = await fetch(url)\n this.layoutData = await res.text()\n if (this.pdfUrl) await this.rebuild()\n }\n\n private async rebuild() {\n if (!this.layoutData) return\n this.blocks = parseBlocks(this.layoutData)\n const md = this.markdownText || this.blocks.map(b => b.text || '').filter(Boolean).join('\\n')\n this.sections = matchMarkdownToPdf(md, this.blocks)\n if (this.pdfUrl) await this.renderPdfPages()\n this.buildUI()\n }\n\n private async renderPdfPages() {\n if (!this.pdfUrl) return\n const pdf = await pdfjsLib.getDocument(this.pdfUrl).promise\n this.pages = []\n for (let i = 1; i <= pdf.numPages; i++) {\n const page = await pdf.getPage(i)\n const vp = page.getViewport({ scale: RENDER_SCALE })\n const cvs = document.createElement('canvas')\n cvs.width = vp.width; cvs.height = vp.height\n await page.render({ canvasContext: cvs.getContext('2d')!, viewport: vp }).promise\n this.pages.push({ p: i, w: vp.width, h: vp.height, src: cvs.toDataURL() })\n page.cleanup()\n }\n pdf.destroy()\n }\n\n private buildUI() {\n const shadow = this.shadowRoot!\n const matched = this.sections.filter(s => s.bbox).length\n shadow.getElementById('stat')!.innerHTML =\n `${this.pages.length} \u9875 | ${this.sections.length} \u884C | <span class=\"${matched > 0 ? 'ok' : 'warn'}\">\u5339\u914D ${matched}</span> | ${this.blocks.length} \u5757`\n this.buildPdfOverlays()\n this.buildMarkdown()\n }\n\n private buildPdfOverlays() {\n const pane = this.shadowRoot!.getElementById('pdfPane')!\n pane.innerHTML = ''\n const containerW = pane.clientWidth - 20\n if (containerW <= 0 || this.pages.length === 0) return\n\n for (const rp of this.pages) {\n const cssW = containerW\n const cssH = rp.h * (containerW / rp.w)\n const pageBlocks = this.blocks.filter(b => b.page_idx === rp.p - 1)\n const pageScale = containerW / rp.w\n const s = RENDER_SCALE * pageScale\n\n const wrapper = document.createElement('div')\n wrapper.className = 'pdf-page'\n wrapper.style.width = cssW + 'px'; wrapper.style.height = cssH + 'px'\n wrapper.dataset.page = String(rp.p)\n\n const img = document.createElement('img'); img.src = rp.src\n wrapper.appendChild(img)\n const label = document.createElement('span'); label.className = 'page-num'; label.textContent = String(rp.p)\n wrapper.appendChild(label)\n\n for (const b of pageBlocks) {\n const [x0, y0, x1, y1] = b.bbox\n const ov = document.createElement('div'); ov.className = 'block-overlay'\n ov.style.left = (x0 * s) + 'px'; ov.style.top = (y0 * s) + 'px'\n ov.style.width = Math.max((x1 - x0) * s, 2) + 'px'\n ov.style.height = Math.max((y1 - y0) * s, 2) + 'px'\n ov.title = (b.text || '').slice(0, 120)\n ov.addEventListener('click', () => this.onBlockClick(b, ov))\n wrapper.appendChild(ov)\n }\n pane.appendChild(wrapper)\n }\n }\n\n private buildMarkdown() {\n const pane = this.shadowRoot!.getElementById('mdPane')!\n pane.innerHTML = ''\n for (let i = 0; i < this.sections.length; i++) {\n const sec = this.sections[i]\n const el = document.createElement('span')\n el.className = 'md-line' + (sec.bbox ? ' match' : ' no-match')\n el.dataset.idx = String(i)\n el.textContent = sec.text\n if (sec.bbox) { const b = document.createElement('span'); b.className = 'badge'; b.textContent = `p${sec.page}`; el.appendChild(b) }\n el.addEventListener('click', () => this.onMdClick(sec, i, el))\n pane.appendChild(el)\n }\n }\n\n private onMdClick(sec: MdSection, idx: number, el: HTMLElement) {\n const shadow = this.shadowRoot!\n shadow.querySelectorAll('.md-line.active').forEach(e => e.classList.remove('active'))\n shadow.querySelectorAll('.block-overlay.active').forEach(e => e.classList.remove('active'))\n el.classList.add('active'); this.activeIdx = idx\n if (sec.bbox) {\n const pageEl = shadow.querySelector(`.pdf-page[data-page=\"${sec.page}\"]`) as HTMLElement | null\n if (pageEl) pageEl.scrollIntoView({ behavior: 'smooth', block: 'start' })\n const overlays = shadow.querySelectorAll(`.pdf-page[data-page=\"${sec.page}\"] .block-overlay`)\n const page = this.pages[sec.page - 1]\n if (page) {\n const pw = shadow.getElementById('pdfPane')!.clientWidth - 20\n const s = RENDER_SCALE * (pw / page.w)\n overlays.forEach(ov => {\n const el2 = ov as HTMLElement\n if (Math.abs(parseFloat(el2.style.left) - sec.bbox![0] * s) < 4) {\n el2.classList.add('active'); el2.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n })\n }\n }\n }\n\n private onBlockClick(block: PdfBlock, ov: HTMLElement) {\n const shadow = this.shadowRoot!\n shadow.querySelectorAll('.md-line.active').forEach(e => e.classList.remove('active'))\n shadow.querySelectorAll('.block-overlay.active').forEach(e => e.classList.remove('active'))\n ov.classList.add('active')\n const blockNorm = normalize(block.text || '')\n let bestIdx = -1, bestSim = 0\n for (let i = 0; i < this.sections.length; i++) {\n if (!this.sections[i].bbox) continue\n const sim = lcsSimilarity(blockNorm, normalize(this.sections[i].text))\n if (sim > bestSim && sim > 0.05) { bestSim = sim; bestIdx = i }\n }\n if (bestIdx >= 0) {\n this.activeIdx = bestIdx\n const el = shadow.querySelector(`[data-idx=\"${bestIdx}\"]`) as HTMLElement | null\n if (el) { el.classList.add('active'); el.scrollIntoView({ behavior: 'smooth', block: 'center' }) }\n }\n }\n}\n\nif (typeof customElements !== 'undefined' && !customElements.get('mineru-layout-viewer')) {\n customElements.define('mineru-layout-viewer', MineruLayoutViewer)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["window"]
|
|
7
|
+
}
|