@heripo/pdf-parser 0.1.27 → 0.1.28
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.ko.md +8 -8
- package/README.md +8 -8
- package/dist/index.cjs +41 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +41 -77
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.ko.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
## 주요 기능
|
|
36
36
|
|
|
37
37
|
- **고품질 OCR**: Docling SDK를 활용한 문서 인식 (ocrmac / Apple Vision Framework)
|
|
38
|
-
-
|
|
38
|
+
- **한국어 보고서 VLM 보정**: 한국어 보고서를 자동 감지하고 모든 페이지에 VLM 텍스트 보정을 적용
|
|
39
39
|
- **Apple Silicon 최적화**: M1/M2/M3/M4/M5 칩에서 GPU 가속 지원
|
|
40
40
|
- **자동 환경 설정**: Python 가상환경 및 docling-serve 자동 설치
|
|
41
41
|
- **이미지 추출**: PDF 내 이미지 자동 추출 및 저장
|
|
@@ -233,16 +233,16 @@ await pdfParser.dispose();
|
|
|
233
233
|
|
|
234
234
|
**ocrmac(Apple Vision Framework)은 매우 우수한 OCR 엔진입니다** -- 무료이고, GPU 가속을 지원하며, 고품질 결과를 제공합니다. 수천~수백만 권의 고고학 보고서를 처리할 때 이만한 솔루션이 없습니다.
|
|
235
235
|
|
|
236
|
-
**그러나
|
|
236
|
+
**그러나 한국어 고고학 보고서는 문자 체계 인식 보정이 자주 필요합니다.** 한자 복원, CJK mojibake, 음독 치환, 기관명 등은 표준 OCR만으로 안정적으로 처리하기 어렵습니다. VLM을 기본 OCR 엔진으로 쓰는 대신, 빠른 ocrmac 파이프라인을 먼저 실행한 뒤 한국어 보고서에 VLM 텍스트 보정을 적용합니다.
|
|
237
237
|
|
|
238
|
-
### 2단계 감지 (`OcrStrategySampler`)
|
|
238
|
+
### 2단계 한국어 감지 (`OcrStrategySampler`)
|
|
239
239
|
|
|
240
|
-
1. **텍스트 레이어 사전 검사** (비용 없음): `pdftotext`로 문서의 텍스트 레이어를 추출하여
|
|
241
|
-
2. **VLM 샘플링** (필요 시에만): 최대 15페이지를 샘플링(앞뒤 10%는 표지·부록으로 제외)하여 Vision LLM으로
|
|
240
|
+
1. **텍스트 레이어 사전 검사** (비용 없음): `pdftotext`로 문서의 텍스트 레이어를 추출하여 한글을 확인합니다. 한글이 있으면 즉시 한국어 문서로 판별합니다.
|
|
241
|
+
2. **VLM 샘플링** (필요 시에만): 최대 15페이지를 샘플링(앞뒤 10%는 표지·부록으로 제외)하여 Vision LLM으로 언어를 판정합니다. `ko-KR` 감지 시 즉시 종료합니다.
|
|
242
242
|
|
|
243
|
-
###
|
|
243
|
+
### 전체 문서 보정 (`VlmTextCorrector`)
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
한국어 보고서가 감지되면, 모든 페이지를 VLM에 전송하여 보정합니다:
|
|
246
246
|
|
|
247
247
|
- 각 페이지의 OCR 텍스트 요소와 표 셀을 추출
|
|
248
248
|
- `pdftotext` 참조 텍스트를 품질 기준으로 활용
|
|
@@ -261,7 +261,7 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
261
261
|
// OCR 전략 샘플링 활성화 (Vision LLM 모델 제공)
|
|
262
262
|
strategySamplerModel: openai('gpt-5.1'),
|
|
263
263
|
|
|
264
|
-
// VLM 텍스트 보정 모델 (
|
|
264
|
+
// VLM 텍스트 보정 모델 (한국어 보고서 감지 시 필요)
|
|
265
265
|
vlmProcessorModel: openai('gpt-5.1'),
|
|
266
266
|
|
|
267
267
|
// VLM 페이지 처리 동시성 (기본값: 1)
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
## Key Features
|
|
36
36
|
|
|
37
37
|
- **High-Quality OCR**: Document recognition using Docling SDK (ocrmac / Apple Vision Framework)
|
|
38
|
-
- **
|
|
38
|
+
- **Korean Report VLM Correction**: Automatically detects Korean reports and applies VLM text correction to all pages
|
|
39
39
|
- **Apple Silicon Optimized**: GPU acceleration on M1/M2/M3/M4/M5 chips
|
|
40
40
|
- **Automatic Environment Setup**: Automatic Python virtual environment and docling-serve installation
|
|
41
41
|
- **Image Extraction**: Automatic extraction and saving of images from PDFs
|
|
@@ -233,16 +233,16 @@ await pdfParser.dispose();
|
|
|
233
233
|
|
|
234
234
|
**ocrmac (Apple Vision Framework) is an excellent OCR engine** -- it's free, GPU-accelerated, and delivers high-quality results. For processing thousands to millions of archaeological reports, there's no better solution.
|
|
235
235
|
|
|
236
|
-
**However,
|
|
236
|
+
**However, Korean archaeological reports often need script-aware correction.** Hanja restoration, CJK mojibake, phonetic substitutions, and institution names can be unreliable with standard OCR alone. Rather than using VLM as the primary OCR engine, the system runs the fast ocrmac pipeline first and then applies VLM text correction to Korean reports.
|
|
237
237
|
|
|
238
|
-
### Two-Stage Detection (`OcrStrategySampler`)
|
|
238
|
+
### Two-Stage Korean Detection (`OcrStrategySampler`)
|
|
239
239
|
|
|
240
|
-
1. **Text Layer Pre-Check** (zero cost): Extracts the document's text layer using `pdftotext` and checks for
|
|
241
|
-
2. **VLM Sampling** (only when needed): Samples up to 15 pages (trimming 10% from front/back to skip covers and appendices) and analyzes them with a Vision LLM. Uses early exit
|
|
240
|
+
1. **Text Layer Pre-Check** (zero cost): Extracts the document's text layer using `pdftotext` and checks for Hangul. If Hangul is present, the document is immediately treated as Korean.
|
|
241
|
+
2. **VLM Sampling** (only when needed): Samples up to 15 pages (trimming 10% from front/back to skip covers and appendices) and analyzes them with a Vision LLM. Uses early exit when `ko-KR` is detected.
|
|
242
242
|
|
|
243
|
-
###
|
|
243
|
+
### Full-Document Correction (`VlmTextCorrector`)
|
|
244
244
|
|
|
245
|
-
When
|
|
245
|
+
When a Korean report is detected, every page is sent to the VLM for correction:
|
|
246
246
|
|
|
247
247
|
- Extracts OCR text elements and table cells from each page
|
|
248
248
|
- Uses `pdftotext` reference text as a quality anchor
|
|
@@ -261,7 +261,7 @@ const tokenUsageReport = await pdfParser.parse(
|
|
|
261
261
|
// Enable OCR strategy sampling (provide a Vision LLM model)
|
|
262
262
|
strategySamplerModel: openai('gpt-5.1'),
|
|
263
263
|
|
|
264
|
-
// VLM model for text correction (required when
|
|
264
|
+
// VLM model for text correction (required when Korean reports are detected)
|
|
265
265
|
vlmProcessorModel: openai('gpt-5.1'),
|
|
266
266
|
|
|
267
267
|
// Concurrency for VLM page processing (default: 1)
|
package/dist/index.cjs
CHANGED
|
@@ -6545,15 +6545,12 @@ var import_model = require("@heripo/model");
|
|
|
6545
6545
|
var import_node_fs12 = require("fs");
|
|
6546
6546
|
var import_v42 = require("zod/v4");
|
|
6547
6547
|
|
|
6548
|
-
// src/prompts/korean-
|
|
6549
|
-
var
|
|
6548
|
+
// src/prompts/korean-document-detection-prompt.ts
|
|
6549
|
+
var KOREAN_DOCUMENT_DETECTION_PROMPT = `Look at this page image carefully and identify all languages present on this page.
|
|
6550
6550
|
|
|
6551
|
-
|
|
6552
|
-
|
|
6551
|
+
Return an array of ocrmac-compatible language tags ordered by prevalence (primary language first).
|
|
6552
|
+
Use ko-KR when Korean text is present, even when the page also contains Hanja (\u6F22\u5B57/Chinese characters), English, numbers, tables, or captions.
|
|
6553
6553
|
|
|
6554
|
-
Answer whether any Hanja characters are present on this page.
|
|
6555
|
-
|
|
6556
|
-
Also identify all languages present on this page. Return an array of ocrmac-compatible language tags ordered by prevalence (primary language first).
|
|
6557
6554
|
Supported tags: ar-SA, ars-SA, cs-CZ, da-DK, de-DE, en-US, es-ES, fr-FR, id-ID, it-IT, ja-JP, ko-KR, ms-MY, nb-NO, nl-NL, nn-NO, no-NO, pl-PL, pt-BR, ro-RO, ru-RU, sv-SE, th-TH, tr-TR, uk-UA, vi-VT, yue-Hans, yue-Hant, zh-Hans, zh-Hant.
|
|
6558
6555
|
Examples: ["ko-KR", "en-US"], ["ja-JP"], ["zh-Hant", "en-US"]`;
|
|
6559
6556
|
|
|
@@ -6561,12 +6558,8 @@ Examples: ["ko-KR", "en-US"], ["ja-JP"], ["zh-Hant", "en-US"]`;
|
|
|
6561
6558
|
var EDGE_TRIM_RATIO = 0.1;
|
|
6562
6559
|
var DEFAULT_MAX_SAMPLE_PAGES = 15;
|
|
6563
6560
|
var DEFAULT_MAX_RETRIES = 3;
|
|
6564
|
-
var CJK_REGEX = /[\u4E00-\u9FFF]/;
|
|
6565
6561
|
var HANGUL_REGEX = /[\uAC00-\uD7AF]/;
|
|
6566
|
-
var
|
|
6567
|
-
hasKoreanHanjaMix: import_v42.z.boolean().describe(
|
|
6568
|
-
"Whether the page contains any Hanja (\u6F22\u5B57/Chinese characters) mixed with Korean text"
|
|
6569
|
-
),
|
|
6562
|
+
var koreanDocumentDetectionSchema = import_v42.z.object({
|
|
6570
6563
|
detectedLanguages: import_v42.z.array(import_v42.z.string()).describe(
|
|
6571
6564
|
'BCP 47 language tags of languages found on this page, ordered by prevalence (e.g., ["ko-KR", "en-US"])'
|
|
6572
6565
|
)
|
|
@@ -6585,14 +6578,14 @@ var OcrStrategySampler = class {
|
|
|
6585
6578
|
*
|
|
6586
6579
|
* @param pdfPath - Path to the PDF file
|
|
6587
6580
|
* @param outputDir - Directory for temporary rendered pages
|
|
6588
|
-
* @param model - Vision language model for Korean
|
|
6581
|
+
* @param model - Vision language model for Korean document detection
|
|
6589
6582
|
* @param options - Sampling options
|
|
6590
6583
|
* @returns OcrStrategy with method ('ocrmac' or 'vlm') and metadata
|
|
6591
6584
|
*/
|
|
6592
6585
|
async sample(pdfPath, outputDir, model, options) {
|
|
6593
6586
|
const maxSamplePages = options?.maxSamplePages ?? DEFAULT_MAX_SAMPLE_PAGES;
|
|
6594
6587
|
this.logger.info("[OcrStrategySampler] Starting OCR strategy sampling...");
|
|
6595
|
-
const preCheckResult = await this.
|
|
6588
|
+
const preCheckResult = await this.preCheckKoreanFromTextLayer(pdfPath);
|
|
6596
6589
|
if (preCheckResult) {
|
|
6597
6590
|
return preCheckResult;
|
|
6598
6591
|
}
|
|
@@ -6623,9 +6616,9 @@ var OcrStrategySampler = class {
|
|
|
6623
6616
|
model,
|
|
6624
6617
|
options
|
|
6625
6618
|
);
|
|
6626
|
-
if (sampleResult.
|
|
6619
|
+
if (sampleResult.foundKorean) {
|
|
6627
6620
|
this.logger.info(
|
|
6628
|
-
`[OcrStrategySampler] Korean
|
|
6621
|
+
`[OcrStrategySampler] Korean document detected on page ${sampleResult.koreanPageNo} \u2192 VLM strategy`
|
|
6629
6622
|
);
|
|
6630
6623
|
const detectedLanguages2 = this.aggregateLanguages(
|
|
6631
6624
|
sampleResult.languageFrequency
|
|
@@ -6633,13 +6626,13 @@ var OcrStrategySampler = class {
|
|
|
6633
6626
|
return {
|
|
6634
6627
|
method: "vlm",
|
|
6635
6628
|
detectedLanguages: detectedLanguages2,
|
|
6636
|
-
reason: `Korean
|
|
6629
|
+
reason: `Korean document detected on page ${sampleResult.koreanPageNo}`,
|
|
6637
6630
|
sampledPages: sampleResult.sampledCount,
|
|
6638
6631
|
totalPages: renderResult.pageCount
|
|
6639
6632
|
};
|
|
6640
6633
|
}
|
|
6641
6634
|
this.logger.info(
|
|
6642
|
-
"[OcrStrategySampler] No Korean
|
|
6635
|
+
"[OcrStrategySampler] No Korean language detected \u2192 ocrmac strategy"
|
|
6643
6636
|
);
|
|
6644
6637
|
const detectedLanguages = this.aggregateLanguages(
|
|
6645
6638
|
sampleResult.languageFrequency
|
|
@@ -6647,13 +6640,13 @@ var OcrStrategySampler = class {
|
|
|
6647
6640
|
return {
|
|
6648
6641
|
method: "ocrmac",
|
|
6649
6642
|
detectedLanguages,
|
|
6650
|
-
reason: `No Korean
|
|
6643
|
+
reason: `No Korean language detected in ${sampleResult.sampledCount} sampled pages`,
|
|
6651
6644
|
sampledPages: sampleResult.sampledCount,
|
|
6652
6645
|
totalPages: renderResult.pageCount
|
|
6653
6646
|
};
|
|
6654
6647
|
}
|
|
6655
6648
|
/**
|
|
6656
|
-
* Sample pages for Korean
|
|
6649
|
+
* Sample pages for Korean language detection with early exit.
|
|
6657
6650
|
*/
|
|
6658
6651
|
async samplePages(sampleIndices, pageFiles, model, options) {
|
|
6659
6652
|
const languageFrequency = /* @__PURE__ */ new Map();
|
|
@@ -6669,72 +6662,52 @@ var OcrStrategySampler = class {
|
|
|
6669
6662
|
for (const lang of pageAnalysis.detectedLanguages) {
|
|
6670
6663
|
languageFrequency.set(lang, (languageFrequency.get(lang) ?? 0) + 1);
|
|
6671
6664
|
}
|
|
6672
|
-
if (pageAnalysis.
|
|
6665
|
+
if (pageAnalysis.detectedLanguages.includes("ko-KR")) {
|
|
6673
6666
|
return {
|
|
6674
|
-
|
|
6675
|
-
|
|
6667
|
+
foundKorean: true,
|
|
6668
|
+
koreanPageNo: idx + 1,
|
|
6676
6669
|
sampledCount: i + 1,
|
|
6677
6670
|
languageFrequency
|
|
6678
6671
|
};
|
|
6679
6672
|
}
|
|
6680
6673
|
}
|
|
6681
6674
|
return {
|
|
6682
|
-
|
|
6675
|
+
foundKorean: false,
|
|
6683
6676
|
sampledCount: sampleIndices.length,
|
|
6684
6677
|
languageFrequency
|
|
6685
6678
|
};
|
|
6686
6679
|
}
|
|
6687
6680
|
/**
|
|
6688
|
-
* Pre-check for
|
|
6689
|
-
* Extracts full document text in a single process and checks at document level
|
|
6690
|
-
*
|
|
6691
|
-
* - Hangul + Hanja (anywhere in document) → VLM (confirmed Korean-Hanja mix)
|
|
6692
|
-
* - Hangul only → ocrmac with ko-KR (confirmed Korean)
|
|
6681
|
+
* Pre-check for Korean text in PDF text layer using pdftotext.
|
|
6682
|
+
* Extracts full document text in a single process and checks at document level:
|
|
6683
|
+
* - Hangul anywhere in document → VLM (confirmed Korean document)
|
|
6693
6684
|
* - No Hangul (English, Japanese, etc.) → null (delegates to VLM for language detection)
|
|
6694
6685
|
*/
|
|
6695
|
-
async
|
|
6686
|
+
async preCheckKoreanFromTextLayer(pdfPath) {
|
|
6696
6687
|
try {
|
|
6697
6688
|
const totalPages = await this.textExtractor.getPageCount(pdfPath);
|
|
6698
6689
|
if (totalPages === 0) return null;
|
|
6699
6690
|
const fullText = await this.textExtractor.extractFullText(pdfPath);
|
|
6700
6691
|
if (fullText.trim().length === 0) {
|
|
6701
6692
|
this.logger.debug(
|
|
6702
|
-
"[OcrStrategySampler] No
|
|
6693
|
+
"[OcrStrategySampler] No text in text layer, falling back to VLM sampling"
|
|
6703
6694
|
);
|
|
6704
6695
|
return null;
|
|
6705
6696
|
}
|
|
6706
6697
|
const hasHangul = HANGUL_REGEX.test(fullText);
|
|
6707
|
-
const hasHanja = CJK_REGEX.test(fullText);
|
|
6708
6698
|
if (!hasHangul) {
|
|
6709
6699
|
this.logger.debug(
|
|
6710
6700
|
"[OcrStrategySampler] No Hangul in text layer, falling back to VLM sampling"
|
|
6711
6701
|
);
|
|
6712
6702
|
return null;
|
|
6713
6703
|
}
|
|
6714
|
-
if (hasHanja) {
|
|
6715
|
-
const pageTextArray = fullText.split("\f");
|
|
6716
|
-
const koreanHanjaMixPages = pageTextArray.flatMap(
|
|
6717
|
-
(text, i) => CJK_REGEX.test(text) ? [i + 1] : []
|
|
6718
|
-
);
|
|
6719
|
-
this.logger.info(
|
|
6720
|
-
`[OcrStrategySampler] Hangul-Hanja mix detected in text layer \u2192 VLM strategy (${koreanHanjaMixPages.length} pages with Hanja)`
|
|
6721
|
-
);
|
|
6722
|
-
return {
|
|
6723
|
-
method: "vlm",
|
|
6724
|
-
detectedLanguages: ["ko-KR"],
|
|
6725
|
-
reason: "Hangul-Hanja mix found in PDF text layer",
|
|
6726
|
-
koreanHanjaMixPages,
|
|
6727
|
-
sampledPages: totalPages,
|
|
6728
|
-
totalPages
|
|
6729
|
-
};
|
|
6730
|
-
}
|
|
6731
6704
|
this.logger.info(
|
|
6732
|
-
"[OcrStrategySampler]
|
|
6705
|
+
"[OcrStrategySampler] Korean text detected in text layer \u2192 VLM strategy"
|
|
6733
6706
|
);
|
|
6734
6707
|
return {
|
|
6735
|
-
method: "
|
|
6708
|
+
method: "vlm",
|
|
6736
6709
|
detectedLanguages: ["ko-KR"],
|
|
6737
|
-
reason: `
|
|
6710
|
+
reason: `Korean text found in PDF text layer (${totalPages} pages checked)`,
|
|
6738
6711
|
sampledPages: totalPages,
|
|
6739
6712
|
totalPages
|
|
6740
6713
|
};
|
|
@@ -6775,21 +6748,24 @@ var OcrStrategySampler = class {
|
|
|
6775
6748
|
);
|
|
6776
6749
|
}
|
|
6777
6750
|
/**
|
|
6778
|
-
* Analyze a single sample page for
|
|
6751
|
+
* Analyze a single sample page for language.
|
|
6779
6752
|
* Normalizes raw VLM language responses to valid BCP 47 tags, filtering out invalid ones.
|
|
6780
6753
|
*
|
|
6781
|
-
* @returns Object with
|
|
6754
|
+
* @returns Object with normalized detected languages
|
|
6782
6755
|
*/
|
|
6783
6756
|
async analyzeSamplePage(pageFile, pageNo, model, options) {
|
|
6784
6757
|
this.logger.debug(
|
|
6785
|
-
`[OcrStrategySampler] Analyzing page ${pageNo} for
|
|
6758
|
+
`[OcrStrategySampler] Analyzing page ${pageNo} for language...`
|
|
6786
6759
|
);
|
|
6787
6760
|
const imageData = new Uint8Array((0, import_node_fs12.readFileSync)(pageFile));
|
|
6788
6761
|
const messages = [
|
|
6789
6762
|
{
|
|
6790
6763
|
role: "user",
|
|
6791
6764
|
content: [
|
|
6792
|
-
{
|
|
6765
|
+
{
|
|
6766
|
+
type: "text",
|
|
6767
|
+
text: KOREAN_DOCUMENT_DETECTION_PROMPT
|
|
6768
|
+
},
|
|
6793
6769
|
{
|
|
6794
6770
|
type: "image",
|
|
6795
6771
|
image: imageData,
|
|
@@ -6799,7 +6775,7 @@ var OcrStrategySampler = class {
|
|
|
6799
6775
|
}
|
|
6800
6776
|
];
|
|
6801
6777
|
const result = await LLMCaller.callVision({
|
|
6802
|
-
schema:
|
|
6778
|
+
schema: koreanDocumentDetectionSchema,
|
|
6803
6779
|
messages,
|
|
6804
6780
|
primaryModel: model,
|
|
6805
6781
|
fallbackModel: options?.fallbackModel,
|
|
@@ -6807,7 +6783,7 @@ var OcrStrategySampler = class {
|
|
|
6807
6783
|
temperature: options?.temperature ?? 0,
|
|
6808
6784
|
abortSignal: options?.abortSignal,
|
|
6809
6785
|
component: "OcrStrategySampler",
|
|
6810
|
-
phase: "korean-
|
|
6786
|
+
phase: "korean-document-detection"
|
|
6811
6787
|
});
|
|
6812
6788
|
if (options?.aggregator) {
|
|
6813
6789
|
options.aggregator.track(result.usage);
|
|
@@ -6815,10 +6791,9 @@ var OcrStrategySampler = class {
|
|
|
6815
6791
|
const output = result.output;
|
|
6816
6792
|
const normalizedLanguages = output.detectedLanguages.map(import_model.normalizeToBcp47).filter((tag) => tag !== null);
|
|
6817
6793
|
this.logger.debug(
|
|
6818
|
-
`[OcrStrategySampler] Page ${pageNo}:
|
|
6794
|
+
`[OcrStrategySampler] Page ${pageNo}: detectedLanguages=${normalizedLanguages.join(",")}`
|
|
6819
6795
|
);
|
|
6820
6796
|
return {
|
|
6821
|
-
hasKoreanHanjaMix: output.hasKoreanHanjaMix,
|
|
6822
6797
|
detectedLanguages: normalizedLanguages
|
|
6823
6798
|
};
|
|
6824
6799
|
}
|
|
@@ -7067,16 +7042,7 @@ var VlmTextCorrector = class {
|
|
|
7067
7042
|
pagesFailed: 0
|
|
7068
7043
|
};
|
|
7069
7044
|
}
|
|
7070
|
-
|
|
7071
|
-
if (options?.koreanHanjaMixPages && options.koreanHanjaMixPages.length > 0) {
|
|
7072
|
-
const hanjaSet = new Set(options.koreanHanjaMixPages);
|
|
7073
|
-
pageNumbers = allPageNumbers.filter((p) => hanjaSet.has(p));
|
|
7074
|
-
this.logger.info(
|
|
7075
|
-
`[VlmTextCorrector] Filtering to ${pageNumbers.length} Korean-Hanja mix pages out of ${allPageNumbers.length} total`
|
|
7076
|
-
);
|
|
7077
|
-
} else {
|
|
7078
|
-
pageNumbers = allPageNumbers;
|
|
7079
|
-
}
|
|
7045
|
+
const pageNumbers = allPageNumbers;
|
|
7080
7046
|
const concurrency = options?.concurrency ?? DEFAULT_CONCURRENCY;
|
|
7081
7047
|
this.logger.info(
|
|
7082
7048
|
`[VlmTextCorrector] Processing ${pageNumbers.length} pages (concurrency: ${concurrency})...`
|
|
@@ -7281,7 +7247,7 @@ var VlmConversionPipeline = class {
|
|
|
7281
7247
|
* Wrap the original callback with VLM text correction.
|
|
7282
7248
|
* Returns a new callback that runs VLM correction before calling the original.
|
|
7283
7249
|
*/
|
|
7284
|
-
wrapCallback(pdfPath, options, originalCallback, abortSignal, detectedLanguages
|
|
7250
|
+
wrapCallback(pdfPath, options, originalCallback, abortSignal, detectedLanguages) {
|
|
7285
7251
|
if (!options.vlmProcessorModel) {
|
|
7286
7252
|
throw new Error("vlmProcessorModel is required when OCR strategy is VLM");
|
|
7287
7253
|
}
|
|
@@ -7313,8 +7279,7 @@ var VlmConversionPipeline = class {
|
|
|
7313
7279
|
abortSignal,
|
|
7314
7280
|
onTokenUsage: options.onTokenUsage,
|
|
7315
7281
|
documentLanguages: detectedLanguages,
|
|
7316
|
-
pageTexts
|
|
7317
|
-
koreanHanjaMixPages
|
|
7282
|
+
pageTexts
|
|
7318
7283
|
}
|
|
7319
7284
|
);
|
|
7320
7285
|
await originalCallback(outputDir);
|
|
@@ -7466,8 +7431,7 @@ var PDFConverter = class {
|
|
|
7466
7431
|
trackedOptions,
|
|
7467
7432
|
postCorrectionCallback,
|
|
7468
7433
|
abortSignal,
|
|
7469
|
-
strategy.detectedLanguages
|
|
7470
|
-
strategy.koreanHanjaMixPages
|
|
7434
|
+
strategy.detectedLanguages
|
|
7471
7435
|
);
|
|
7472
7436
|
const vlmOptions = strategy.detectedLanguages ? { ...trackedOptions, ocr_lang: strategy.detectedLanguages } : trackedOptions;
|
|
7473
7437
|
await this.convert(
|
|
@@ -8002,7 +7966,7 @@ var META_DESCRIPTION_PATTERNS_EN = [
|
|
|
8002
7966
|
var REPETITIVE_PATTERN_RATIO_THRESHOLD = 0.3;
|
|
8003
7967
|
var REPETITIVE_PATTERN_MIN_REPEATS = 5;
|
|
8004
7968
|
var HANGUL_REGEX2 = /[\uAC00-\uD7AF\u1100-\u11FF]/g;
|
|
8005
|
-
var
|
|
7969
|
+
var CJK_REGEX = /[\u4E00-\u9FFF]/g;
|
|
8006
7970
|
var VlmResponseValidator = class {
|
|
8007
7971
|
/**
|
|
8008
7972
|
* Validate VLM page result quality.
|
|
@@ -8070,7 +8034,7 @@ var VlmResponseValidator = class {
|
|
|
8070
8034
|
return null;
|
|
8071
8035
|
}
|
|
8072
8036
|
const hangulCount = allContent.match(HANGUL_REGEX2)?.length ?? 0;
|
|
8073
|
-
const cjkCount = allContent.match(
|
|
8037
|
+
const cjkCount = allContent.match(CJK_REGEX)?.length ?? 0;
|
|
8074
8038
|
const koreanCjkCount = hangulCount + cjkCount;
|
|
8075
8039
|
const ratio = koreanCjkCount / nonWhitespace.length;
|
|
8076
8040
|
if (ratio < KOREAN_SCRIPT_RATIO_THRESHOLD) {
|