专刊
This commit is contained in:
1
scripts/_figure-extract-debug.html
Normal file
1
scripts/_figure-extract-debug.html
Normal file
File diff suppressed because one or more lines are too long
1
scripts/_out.html
Normal file
1
scripts/_out.html
Normal file
File diff suppressed because one or more lines are too long
15
scripts/alias-hooks.mjs
Normal file
15
scripts/alias-hooks.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { pathToFileURL } from 'url';
|
||||
|
||||
export async function resolve(specifier, context, nextResolve) {
|
||||
if (specifier.startsWith('@/')) {
|
||||
let abs = path.join(process.cwd(), 'src', specifier.slice(2));
|
||||
if (!fs.existsSync(abs) && !path.extname(abs)) {
|
||||
if (fs.existsSync(abs + '.js')) abs += '.js';
|
||||
else if (fs.existsSync(path.join(abs, 'index.js'))) abs = path.join(abs, 'index.js');
|
||||
}
|
||||
return { shortCircuit: true, url: pathToFileURL(abs).href };
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
19
scripts/alias-loader.mjs
Normal file
19
scripts/alias-loader.mjs
Normal file
@@ -0,0 +1,19 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { pathToFileURL } from 'url';
|
||||
|
||||
export async function resolve(specifier, context, nextResolve) {
|
||||
if (specifier.startsWith('@/')) {
|
||||
let rel = specifier.slice(2);
|
||||
let abs = path.join(process.cwd(), 'src', rel);
|
||||
if (!fs.existsSync(abs) && !path.extname(abs)) {
|
||||
if (fs.existsSync(abs + '.js')) abs = abs + '.js';
|
||||
else if (fs.existsSync(path.join(abs, 'index.js'))) abs = path.join(abs, 'index.js');
|
||||
}
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(abs).href
|
||||
};
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
5
scripts/alias-register.mjs
Normal file
5
scripts/alias-register.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
import { register } from 'node:module';
|
||||
import { pathToFileURL } from 'url';
|
||||
import path from 'path';
|
||||
|
||||
register(pathToFileURL(path.join(process.cwd(), 'scripts/alias-hooks.mjs')).href);
|
||||
45
scripts/inspect-word-fig-pairing.mjs
Normal file
45
scripts/inspect-word-fig-pairing.mjs
Normal file
@@ -0,0 +1,45 @@
|
||||
import FS from 'fs';
|
||||
import path from 'path';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
const file = path.join(
|
||||
'C:/Users/Administrator/Desktop/IN2026A0418001_final_20260713 1.docx'
|
||||
);
|
||||
const xml = await (await JSZip.loadAsync(FS.readFileSync(file))).file('word/document.xml').async('string');
|
||||
const paras = xml.split(/<\/w:p>/);
|
||||
|
||||
function plainOf(p) {
|
||||
return [...p.matchAll(/<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g)]
|
||||
.map((x) => x[1])
|
||||
.join('')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function blipCount(p) {
|
||||
return (p.match(/a:blip/g) || []).length;
|
||||
}
|
||||
|
||||
function drawingCount(p) {
|
||||
return (p.match(/<w:drawing/g) || []).length;
|
||||
}
|
||||
|
||||
for (let i = 0; i < paras.length; i++) {
|
||||
const plain = plainOf(paras[i]);
|
||||
if (!/^Figure\s+\d+/i.test(plain)) continue;
|
||||
// look back up to 8 paras for images / A B labels
|
||||
const back = [];
|
||||
for (let j = Math.max(0, i - 8); j < i; j++) {
|
||||
const t = plainOf(paras[j]);
|
||||
const b = blipCount(paras[j]);
|
||||
const d = drawingCount(paras[j]);
|
||||
if (b || d || t) {
|
||||
back.push({ j, blips: b, drawings: d, text: t.slice(0, 80) });
|
||||
}
|
||||
}
|
||||
const next = plainOf(paras[i + 1] || '').slice(0, 180);
|
||||
console.log('\nCAPTION', plain.slice(0, 100));
|
||||
console.log('BACK', JSON.stringify(back, null, 0));
|
||||
console.log('NOTE?', next);
|
||||
}
|
||||
82
scripts/inspect-word-figures.mjs
Normal file
82
scripts/inspect-word-figures.mjs
Normal file
@@ -0,0 +1,82 @@
|
||||
import FS from 'fs';
|
||||
import path from 'path';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
const file = path.join(
|
||||
'C:/Users/Administrator/Desktop/IN2026A0418001_final_20260713 1.docx'
|
||||
);
|
||||
|
||||
const buf = FS.readFileSync(file);
|
||||
const zip = await JSZip.loadAsync(buf);
|
||||
const xml = await zip.file('word/document.xml').async('string');
|
||||
|
||||
const drawings = (xml.match(/<w:drawing/g) || []).length;
|
||||
const blips = (xml.match(/a:blip/g) || []).length;
|
||||
console.log('drawings', drawings, 'blips', blips);
|
||||
|
||||
const colors = {};
|
||||
for (const m of xml.matchAll(/w:color\s+w:val="([^"]+)"/g)) {
|
||||
colors[m[1]] = (colors[m[1]] || 0) + 1;
|
||||
}
|
||||
console.log(
|
||||
'colors top',
|
||||
Object.entries(colors)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 20)
|
||||
);
|
||||
|
||||
// Extract paragraphs containing "Figure" with nearby text
|
||||
const paras = xml.split(/<\/w:p>/);
|
||||
let figParaCount = 0;
|
||||
for (let i = 0; i < paras.length; i++) {
|
||||
const p = paras[i];
|
||||
const texts = [...p.matchAll(/<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g)].map((x) => x[1]);
|
||||
const plain = texts.join('').replace(/&/g, '&').trim();
|
||||
if (!/^Figure\s+\d+/i.test(plain) && !/^Fig\.?\s*\d+/i.test(plain)) continue;
|
||||
figParaCount += 1;
|
||||
const hasDrawing = /<w:drawing/.test(p);
|
||||
const colorVals = [...p.matchAll(/w:color\s+w:val="([^"]+)"/g)].map((x) => x[1]);
|
||||
// next non-empty para
|
||||
let nextPlain = '';
|
||||
for (let j = i + 1; j < Math.min(i + 5, paras.length); j++) {
|
||||
const nt = [...paras[j].matchAll(/<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g)]
|
||||
.map((x) => x[1])
|
||||
.join('')
|
||||
.replace(/&/g, '&')
|
||||
.trim();
|
||||
if (nt) {
|
||||
nextPlain = nt.slice(0, 220);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// prev: look back for drawing-only paragraphs
|
||||
let prevHasDrawing = false;
|
||||
for (let j = i - 1; j >= Math.max(0, i - 6); j--) {
|
||||
if (/<w:drawing/.test(paras[j])) {
|
||||
prevHasDrawing = true;
|
||||
break;
|
||||
}
|
||||
const pt = [...paras[j].matchAll(/<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g)]
|
||||
.map((x) => x[1])
|
||||
.join('')
|
||||
.trim();
|
||||
if (pt && !/^[AB]$/.test(pt)) break;
|
||||
}
|
||||
console.log('---');
|
||||
console.log('caption:', plain.slice(0, 160));
|
||||
console.log('colors:', [...new Set(colorVals)]);
|
||||
console.log('sameParaDrawing:', hasDrawing, 'prevHasDrawing:', prevHasDrawing);
|
||||
console.log('next:', nextPlain);
|
||||
}
|
||||
console.log('figParaCount', figParaCount);
|
||||
|
||||
// Check standalone A/B label paragraphs near figures
|
||||
let abCount = 0;
|
||||
for (const p of paras) {
|
||||
const plain = [...p.matchAll(/<w:t(?:\s[^>]*)?>([\s\S]*?)<\/w:t>/g)]
|
||||
.map((x) => x[1])
|
||||
.join('')
|
||||
.trim();
|
||||
if (/^[AB]$/.test(plain)) abCount += 1;
|
||||
}
|
||||
console.log('standalone A/B paras', abCount);
|
||||
29
scripts/test-extract-figures.mjs
Normal file
29
scripts/test-extract-figures.mjs
Normal file
@@ -0,0 +1,29 @@
|
||||
import FS from 'fs';
|
||||
import path from 'path';
|
||||
import { JSDOM } from 'jsdom';
|
||||
|
||||
const { window } = new JSDOM('<!DOCTYPE html><html><body></body></html>');
|
||||
global.window = window;
|
||||
global.document = window.document;
|
||||
global.DOMParser = window.DOMParser;
|
||||
global.Node = window.Node;
|
||||
global.HTMLElement = window.HTMLElement;
|
||||
global.FileReader = window.FileReader;
|
||||
global.Blob = window.Blob;
|
||||
|
||||
const { parseWordFigures } = await import('../src/utils/wordFigureImport.js');
|
||||
|
||||
const filePath = path.join(
|
||||
'C:/Users/Administrator/Desktop/IN2026A0418001_final_20260713 1.docx'
|
||||
);
|
||||
const buf = FS.readFileSync(filePath);
|
||||
const result = await parseWordFigures(buf);
|
||||
console.log('parsedCount', result.parsedCount);
|
||||
result.figures.forEach((f, idx) => {
|
||||
console.log('----', idx + 1);
|
||||
console.log('num', f.figureNumber);
|
||||
console.log('title', f.title.slice(0, 130));
|
||||
console.log('note', (f.note || '').slice(0, 150));
|
||||
console.log('imgs', f.imageCount);
|
||||
console.log('red', /data-word-caption-color="red"|#d25a5a/i.test(f.titleHtml || ''));
|
||||
});
|
||||
35
scripts/test-fig-match.mjs
Normal file
35
scripts/test-fig-match.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
extractFigureNumbersFromText,
|
||||
paragraphMentionsFigureNumber,
|
||||
parseFigureNumberFromTitle,
|
||||
findFirstAmIdForFigureNumber
|
||||
} from '../src/utils/manuscriptMediaReferences.js';
|
||||
|
||||
const samples = [
|
||||
'Figure 3A and Figure 3B.',
|
||||
'as shown in Figure 3A and Figure 3B.',
|
||||
'See Fig. 3 and Fig. 3A',
|
||||
'as shown in <blue>Figure 3A</blue> and <blue>Figure 3B</blue>.'
|
||||
];
|
||||
for (const s of samples) {
|
||||
console.log(JSON.stringify(s), '=>', extractFigureNumbersFromText(s), 'mentions3', paragraphMentionsFigureNumber(s, 3));
|
||||
}
|
||||
console.log(
|
||||
'title',
|
||||
parseFigureNumberFromTitle('<span style="color:#d25a5a">Figure 3 Co-occurrence map</span>')
|
||||
);
|
||||
console.log(
|
||||
'find',
|
||||
findFirstAmIdForFigureNumber(
|
||||
[
|
||||
{ am_id: 1, type: 0, content: 'hello' },
|
||||
{
|
||||
am_id: 146120,
|
||||
type: 0,
|
||||
content: 'as shown in <blue>Figure 3A</blue> and <blue>Figure 3B</blue>.'
|
||||
},
|
||||
{ am_id: 3, type: 1, content: '', ami_id: 99 }
|
||||
],
|
||||
3
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user