OpenSource-Hub

pdf-inspector

라이브러리

firecrawl/pdf-inspector

PDF 분류, 텍스트 추출 및 Markdown 변환을 위한 빠른 Rust 라이브러리입니다.

개요

고성능 Rust 라이브러리로, PDF가 텍스트형인지 스캔본인지 감지하고, 위치 정보가 포함된 텍스트를 추출하여 OCR 없이 깔끔한 Markdown으로 변환합니다. Python, Node.js 및 WebAssembly 바인딩과 CLI 도구를 제공하여 스마트 라우팅에 활용할 수 있으며, 대부분의 텍스트형 PDF에 대한 OCR 처리를 건너뜁니다.

README 미리보기

# pdf-inspector\n\n[](https://crates.io/crates/pdf-inspector)\n[](https://www.npmjs.com/package/@firecrawl/pdf-inspector)\n[](https://pypi.org/project/pdf-inspector/)\n[](LICENSE)\n\nFast Rust library for PDF classification and text extraction. Detects whether a PDF is text-based or scanned, extracts text with position awareness, and converts to clean Markdown — all without OCR. Includes bindings for [Python](docs/python.md), [Node.js](napi/README.md), and [browser WebAssembly](wasm/README.md).\n\nBuilt by [Firecrawl](https://firecrawl.dev) to handle text-based PDFs locally in under 200ms, skipping expensive OCR services for the ~54% of PDFs that don't need them.\n\n## Features\n\n- **Smart classification** — Detect TextBased, Scanned, ImageBased, or Mixed PDFs in ~10-50ms by sampling content streams. Returns a confidence score (0.0-1.0) and per-page OCR routing.\n- **Text extraction** — Position-aware extraction with font info, X/Y coordinates, and automatic multi-column reading order.\n- **Markdown conversion** — Headings (H1-H4 via font size ratios), bullet/numbered/letter lists, code blocks (monospace font detection), tables (rectangle-based and heuristic), bold/italic formatting, URL linking, and page breaks.\n- **Table detection** — Dual-mode: rectangle-based detection from PDF drawing ops, plus heuristic detection from text alignment. Handles financial tables, footnotes, and continuation tables across pages.\n- **CID font support** — ToUnicode CMap decoding for Type0/Identity-H fonts, UTF-16BE, UTF-8, and Latin-1 encodings.\n- **Multi-column layout** — Automatic detection of newspaper-style columns, sequential reading order, and RTL text support.\n- **Encoding issue detection** — Automatically flags broken font encodings so callers can fall back to OCR.\n- **Single document load** — The document is parsed once and shared between detection and extraction, avoiding redundant I/O.\n- **Browser WebAssembly** — Run the same Rust parser locally in browsers and Web 

FAQ (5)

문제 해결
AWS Lambda 또는 Amazon Linux 2023에서 @firecrawl/pdf-inspector를 로드할 때 발생하는 “GLIBC_2.35 not found” 오류는 어떻게 해결하나요?

1.13.0–1.17.0 버전의 x64 GNU 바이너리는 --use-napi-cross 없이 빌드되었으므로 GLIBC_2.35가 필요하며, Amazon Linux 2023, RHEL 9, Rocky Linux 9와 같은 glibc 2.34 호스트에서 실패합니다. 오해를 불러일으키는 Cannot find module 오류는 loadErrors에서 실제 GLIBC_2.35 not found 실패를 가릴 수 있습니다.

영구적인 릴리스 수정: .github/workflows/publish.yml에서 napi-cross를 사용하여 x86_64-unknown-linux-gnu 타겟을 빌드하세요:

- runner: blacksmith-8vcpu-ubuntu-2404
  target: x86_64-unknown-linux-gnu
  build-flags: --use-napi-cross

그런 다음 다시 빌드하세요:

bunx napi build --platform --release \
  --target x86_64-unknown-linux-gnu --use-napi-cross

결과 바이너리를 확인하세요:

objdump -T pdf-inspector.linux-x64-gnu.node \
  | grep -o 'GLIBC_[0-9.]*' | sort -Vu | tail -1
# 예상: GLIBC_2.17

glibc 2.34 호스트에서의 임시 해결 방법: x64 플랫폼 패키지를 GLIBC_2.34 요구 사항이 있는 마지막 릴리스인 1.12.0 버전으로 고정하거나, 위 명령어로 로컬에서 다시 빌드한 바이너리를 사용하세요. 패키지가 전이적으로 가져와지는 경우 npm override를 사용할 수 있습니다:

{
  "overrides": {
    "@firecrawl/pdf-inspector-linux-x64-gnu": "1.12.0"
  }
}

1.12.0 버전도 여전히 GLIBC_2.34가 필요하므로 Debian 11과 같은 오래된 시스템에는 도움이 되지 않습니다. 이러한 시스템에는 다시 빌드된 GLIBC_2.17 바이너리나 수정된 릴리스가 필요합니다. 이 문제를 우회하기 위해 musl 빌드로 전환하지 마세요.

원본 Issue #505
문제 해결
pdf-inspector에서 일본어 CFF PDF 추출 시 "針針針"처럼 관련 없는 한자가 반복되어 표시되는 문제를 어떻게 해결하나요?

이 문제는 try_remap_subset_cmap가 CIDFontType0 (CFF) 폰트에 TrueType subset-GID 복구를 적용하기 때문에 발생합니다. CFF 폰트에서 CID는 CFF charset을 통해 해석되며 번호가 다시 부여된 glyph ID가 아니므로, 원래의 /ToUnicode CMap을 재매핑해서는 안 됩니다.

fix(tounicode): skip subset GID remap for CIDFontType0 (CFF) descendants PR이 포함된 릴리스로 업그레이드하거나, 패치된 소스에서 pdf2md/pdf-inspector를 빌드하여 수정하세요.

src/tounicode.rs를 직접 패치하는 경우, W-array/순차 재매핑 검사 전에 try_remap_subset_cmap에 가드를 추가하세요:

// Keep the existing ToUnicode CMap unchanged for CFF fonts.
if descendant_subtype == "CIDFontType0" {
    return None;
}

기존 remap_to_sequential 경로는 CIDFontType2 (TrueType) descendant에 대해서만 활성화된 상태로 두세요. 그런 다음 다시 빌드하세요:

cargo build --release
cargo run --bin pdf2md -- input.pdf > out.md

영향을 받는 버전에는 @firecrawl/pdf-inspector 1.11.2 및 @firecrawl/pdf-inspector-wasm 0.1.3이 포함됩니다. 임시 해결 방법으로, pdfjs-dist 6.0.227의 getTextContent()를 사용하여 PDF를 추출하세요. 이 손상은 유효하지만 잘못된 한자를 생성할 수 있으므로, U+FFFD 대체 문자를 세는 방식으로는 이를 감지할 수 없다는 점에 유의하세요.

원본 Issue #208
문제 해결
bare LF(19바이트 스트라이드)를 사용하는 클래식 xref 엔트리로 인해 발생하는 PDF 파싱 오류 "invalid file trailer"를 어떻게 수정하나요?

PR #508이 포함된 릴리스 또는 빌드로 pdf-inspector를 업그레이드하세요. 이 수정은 항목이 단일 LF 바이트로 끝나는(필수적인 20바이트 SP LF 종결 대신 19바이트) 클래식 교차 참조 테이블을 감지하고, 파일의 나머지 부분은 그대로 보존하면서 사양을 준수하는 SP LF 항목으로 테이블을 제자리에서 재구성합니다. 이는 startxref 포인터 복구와는 별개의 문제입니다. 임시 해결 방법으로, pypdf와 같은 관대한 리더를 사용해 PDF를 다시 작성하여 xref 테이블이 재생성되도록 하세요:

from pypdf import PdfReader, PdfWriter

reader = PdfReader("input.pdf")
writer = PdfWriter(clone_from=reader)

with open("fixed.pdf", "wb") as f:
    writer.write(f)

원본 Issue #473
문제 해결
pdf-inspector가 PDF에서 Cámara → CÆmara, código → cdigo와 같은 스페인어 악센트 문자를 누락하거나 잘못 변환하는 문제를 해결하는 방법은?

이 문제는 standard-14 Type1 폰트가 /Differences 배열 없이 << /BaseEncoding /WinAnsiEncoding >>과 같은 인코딩 딕셔너리를 사용할 때 pdf-inspector 1.20.0에서 발생합니다. pdf-inspector는 /BaseEncoding을 무시하고 텍스트를 StandardEncoding으로 디코딩하므로, 0xC0–0xFF 범위의 바이트가 잘못된 글리프로 변환되거나 누락됩니다. has_encoding_issues가 false로 유지되는 것 역시 버그입니다.

영구적인 해결 방법: 폰트 인코딩 수정 사항 "decode simple fonts through their base and built-in encodings and glyph names"가 포함된 릴리스로 pdf-inspector를 업그레이드한 다음 파일을 다시 추출하세요.

임시 해결 방법:

1. 이 인코딩을 올바르게 처리하는 pypdf 6.10 이상을 사용하여 추출합니다:

pip install -U "pypdf>=6.10"

from pypdf import PdfReader

reader = PdfReader("repro.pdf")
text = "\n".join(page.extract_text() for page in reader.pages)
print(text)

2. 또는 /BaseEncoding이 이미 포함된 인코딩 딕셔너리에 빈 /Differences [] 배열을 추가하여 PDF를 정규화합니다:

import pikepdf

with pikepdf.open("repro.pdf") as pdf:
    for page in pdf.pages:
        resources = page.get("/Resources")
        if not isinstance(resources, pikepdf.Dictionary):
            continue
        fonts = resources.get("/Font")
        if not isinstance(fonts, pikepdf.Dictionary):
            continue
        for font in fonts.values():
            encoding = font.get("/Encoding")
            if (
                isinstance(encoding, pikepdf.Dictionary)
                and "/Differences" not in encoding
            ):
                encoding.Differences = pikepdf.Array()
    pdf.save("repro-fixed.pdf")

그런 다음 repro-fixed.pdf에 대해 pdf-inspector를 실행합니다. 빈 /Differences 배열은 유효하며, 문제가 있는 버전이 /BaseEncoding /WinAnsiEncoding을 참조하도록 강제합니다.

원본 Issue #544
문제 해결
작은 네이티브 텍스트 헤더가 있는 스캔된 PDF 페이지에 대해 extract_pages_markdown()이 needs_ocr=False를 보고하는 이유는 무엇인가요?

이 문제는 페이지가 대부분 전체 페이지 래스터 이미지이면서 헤더, 스탬프, 매스트헤드와 같은 소량의 유효한 네이티브 텍스트를 포함하는 경우에 발생합니다. 이전 extract_pages_markdown() 버전에서는 needs_ocr을 이미지 커버리지가 아닌 텍스트 품질 신호만으로 계산했기 때문에, 디코딩 가능한 헤더로 인해 해당 페이지가 OCR이 필요 없는 것으로 잘못 표시되었습니다. classify_pdf()는 해당 페이지를 image_based로 올바르게 감지합니다.

해결 방법: OCR 라우팅이 공유 분류 로직을 사용하고 스캔 위에 매스트헤드가 있는 페이지가 OCR로 전송되도록 수정된 issue #227의 수정 사항이 포함된 릴리스로 pdf-inspector를 업그레이드하세요:

pip install --upgrade pdf-inspector

영향을 받는 버전에서의 우회 방법: 페이지별 needs_ocr 필드 대신 classify_pdf_bytes().pages_needing_ocr을 신뢰할 수 있는 기준으로 사용하세요:

import pdf_inspector

cls = pdf_inspector.classify_pdf_bytes(data)
ext = pdf_inspector.extract_pages_markdown_bytes(data)

pages_needing_ocr = set(cls.pages_needing_ocr)

for page_number, page in enumerate(ext.pages):
    if page_number in pages_needing_ocr:
        # page.markdown이 비어 있지 않거나 page.needs_ocr이 False이더라도 OCR을 실행합니다.
        ocr_text = run_ocr_for_page(data, page_number)

추출된 Markdown이 비어 있지 않다는 것을 페이지에 OCR이 필요 없다는 증거로 사용하지 마세요. 스캔된 표나 본문이 누락된 채 네이티브 헤더만 포함되어 있을 수 있습니다.

원본 Issue #227