spectre_pdf 1.0.0

Native Rust PDF extraction engine: text, markdown for RAG, AcroForm widgets, image decoding, and encrypted PDFs. Lazy parser, persistent Document handle, no C dependencies.
Documentation
"""
Second pass: clean up the artifacts left by strip_library_refs.py.

The first pass over-substituted inside backtick-delimited code spans
and file paths. This pass fixes the worst ugly results.
"""

from __future__ import annotations

import re
from pathlib import Path

ROOT = Path("/mnt/c/Users/RyanJ/spectre-rs")
TARGETS = [
    ROOT / "src/document.rs",
    ROOT / "src/geom.rs",
    ROOT / "src/lib.rs",
    ROOT / "src/meta.rs",
    ROOT / "src/positioned.rs",
    ROOT / "src/python.rs",
    ROOT / "src/search.rs",
    ROOT / "src/structure.rs",
    ROOT / "src/tables.rs",
    ROOT / "crates/spectre-parse/src/cmap.rs",
    ROOT / "crates/spectre-parse/src/content.rs",
    ROOT / "crates/spectre-parse/src/document.rs",
    ROOT / "crates/spectre-parse/src/encoding.rs",
    ROOT / "crates/spectre-parse/src/error.rs",
    ROOT / "crates/spectre-parse/src/filter.rs",
    ROOT / "crates/spectre-parse/src/lib.rs",
    ROOT / "crates/spectre-parse/src/object.rs",
    ROOT / "crates/spectre-parse/src/parser.rs",
]

# Fix the artifacts. Each tuple: (pattern, replacement).
REPLACEMENTS = [
    # Restore code-ticked type names (the script substituted inside backticks)
    (r"`the legacy parser::", "`lopdf::"),
    (r"the legacy parser-fork", "lopdf-fork"),
    (r"the legacy parser-parse-cost", "lopdf-parse-cost"),
    (r"the legacy parser-patch", "lopdf-patch"),
    (r"\[the legacy parser\]", "[lopdf]"),
    # Reword phrases that came out awkward
    (r"the reference parser's parser", "the reference parser"),
    (r"\bthe standard parser `page\.get_text\(\"words\"\)`", "extract per-word"),
    (r"\bthe standard parser `page\.get_text\(\"blocks\"\)`", "extract per-block"),
    (r"\bthe standard parser `page\.get_text\(\"dict\"\)`", "hierarchical dict output"),
    (r"\bthe standard parser `page\.get_links\(\)`", "extract hyperlinks"),
    (r"\bthe standard parser `page\.annots\(\)`", "extract annotations"),
    (r"\bthe standard parser `page\.widgets\(\)`", "extract form fields"),
    (r"\bthe standard parser `page\.find_tables\(\)`", "table detector"),
    (r"\bthe standard parser `[a-zA-Z\.\(\)_]+`", ""),
    (r"\(`the standard parser` [^)]+\)", ""),
    (r"\bthe standard parser [a-zA-Z\.\(\)_]+ equivalent", ""),
    (r"\b— the standard parser\b", ""),
    (r"\bvs `?the standard parser`?", ""),
    (r"\bmatches `?the standard parser`?", "follows the PDF spec"),
    # Generic cleanup
    (r"\bthe standard parser\b", "the document handle"),
    (r"\bthe standard renderer\b", "the markdown renderer"),
    (r"\bthe legacy parser's\b", "the legacy parser's"),  # no-op marker for code
    # Collapse extra spaces and stray punctuation
    (r"  +", " "),
    (r"\.", "."),
    (r"\,", ","),
    (r"\)", ")"),
    (r"\( \)", ""),
    (r"  \.", "."),
    (r"\s+\n", "\n"),
]


def is_comment_line(line: str) -> bool:
    return line.lstrip().startswith("//")


def main() -> None:
    for path in TARGETS:
        original = path.read_text(encoding="utf-8")
        new_lines = []
        for line in original.splitlines(keepends=True):
            if is_comment_line(line):
                fixed = line
                for pat, repl in REPLACEMENTS:
                    fixed = re.sub(pat, repl, fixed)
                new_lines.append(fixed)
            else:
                new_lines.append(line)
        new_content = "".join(new_lines)
        if new_content != original:
            path.write_text(new_content, encoding="utf-8")
            print(f"  cleaned {path.relative_to(ROOT)}")


if __name__ == "__main__":
    main()