Skip to content

v2.4.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 03:14
· 102 commits to main since this release
ce0deb8

PDF Craft v2.4.0

PDF Craft 2.4.0 is a major release for applications that process and translate books at scale.
It introduces JEV-assisted footnote refinement, controlled parallelism for remote services, a
native async foundation for public workflows, and PCEX v4 as a reusable multi-translation
document.

For users upgrading from 2.3.1, this release changes how footnote extraction, remote-service
capacity, asynchronous integrations, and translated PCEX files are configured. Existing
synchronous applications can continue to use the PDFCraft facade.

Highlights

New: JEV-assisted footnote refinement

PDF Craft can now refine footnote extraction with JEV and an LLM. JEV reviews the page-level
footnote analysis, routes uncertain pages to the LLM, and accepts repaired pages only after
deterministic structure and source-integrity validation.

The footnotes extraction option now has three choices:

  • omit it to disable footnote extraction;
  • use FootnoteOptions() for the traditional algorithm; or
  • add FootnoteRefinement for JEV-assisted review and LLM repair.

The AI-assisted tier is optional. The traditional algorithm remains available without JEV or an
LLM.

from pdf_craft import (
    ConcurrentExecutor, ExtractionOptions, FixedCapacity,
    FootnoteOptions, FootnoteRefinement, JEV, LLM, PDFCraft,
    PDFOptions,
)

craft = PDFCraft(pdf=PDFOptions(ocr=ocr_config))
jev_executor = ConcurrentExecutor(FixedCapacity(8))
llm_executor = ConcurrentExecutor(FixedCapacity(4))

options = ExtractionOptions(
    footnotes=FootnoteOptions(
        refinement=FootnoteRefinement(
            jev=JEV(key="your-jev-key"),
            llm=LLM(
                key="your-llm-key",
                url="https://your-provider.example/v1",
                model="your-model",
                token_encoding="o200k_base",
            ),
            jev_executor=jev_executor,
            llm_executor=llm_executor,
        ),
    ),
)

extraction = craft.extract_pdf("book.pdf", "book.pcex", options)

When JEV and the repair LLM consume one shared provider limit, pass a single executor instead.

See #485.

Controlled parallelism for OCR, translation, and JEV

Remote operations can now run concurrently through the public AsyncExecutor interface.
ConcurrentExecutor(FixedCapacity(n)) supplies a fixed concurrency limit, while custom capacity
providers can implement provider-specific admission policies. The same executor can be shared by
operations that consume one provider quota, and separate executors can isolate independent quotas.

This capacity model applies across Vendor OCR, LLM translation, and JEV:

  • Vendor OCR sends page requests concurrently while keeping PDF rendering and page assembly
    ordered.
  • PCEX and EPUB translation process XML groups across chapter boundaries within a bounded window.
  • JEV page review and the LLM repair it selects can process independent pages concurrently.
  • Retries release their capacity while waiting, so backoff does not occupy a request slot.

Large documents are consumed lazily and pending work remains bounded. Provider failures preserve
their original cause and distinguish retryable failures, rate limits, and errors that must stop
the shared channel.

from pdf_craft import (
    ConcurrentExecutor, FixedCapacity, PDFCraft, PDFOptions, SubmitKind,
)

# Share this executor only when OCR and translation consume the same provider quota.
provider_executor = ConcurrentExecutor(FixedCapacity(8))

craft = PDFCraft(pdf=PDFOptions(
    ocr=vendor_ocr,
    ocr_executor=provider_executor,
))

craft.extract_pdf("book.pdf", "book.pcex")

craft.translate_epub(
    "source.epub",
    "translated.epub",
    target_language="zh",
    submit=SubmitKind.REPLACE,
    llm=llm,
    executor=provider_executor,
)

See #491,
#492, and
#493.

Native async workflows

AsyncPDFCraft provides awaitable PDF extraction, OCR, PCEX translation, Markdown and EPUB
rendering, PDF patching, and EPUB translation. It is the primary facade for servers, notebooks,
and applications that already run an event loop.

The synchronous PDFCraft facade remains available for scripts and other synchronous
applications. Both facades use the same workflows and output formats. Async callbacks run on the
caller's event loop, blocking third-party libraries run outside it, and cancellation waits for
active work and subprocesses to shut down safely.

PDFCraftExtraction is now an opaque document handle. Open and export it through
PDFCraft or AsyncPDFCraft, then pass the handle or a .pcex path to the facade's
translation and rendering methods.

from pdf_craft import AsyncPDFCraft, PDFOptions

craft = AsyncPDFCraft(pdf=PDFOptions(ocr=ocr_config))

extraction = await craft.extract_pdf(
    "book.pdf",
    "book.pcex",
)
await craft.render_markdown(extraction, "book.md")
await craft.render_epub(extraction, "book.epub")  # Requires a TOC in the PCEX.

See #483 and
#484.

PCEX v4: one source, multiple translations

PCEX v4 preserves the extracted source and stores translations as independent layers. A single
.pcex file can contain multiple target languages or multiple translations of the same language,
each identified by its own translation_id. One extraction can therefore support several
languages, revisions, and reading formats without repeating OCR.

New sync and async APIs list and resolve the translations in a PCEX file. Markdown and EPUB
rendering can produce:

  • the original source with RenderMode.SOURCE;
  • one selected translation with RenderMode.REPLACE; or
  • source and translation together with RenderMode.BILINGUAL.

Translation layers include chapter text, document metadata, optional page furniture, and translated
image and table fields. Coverage information controls source fallback when only part of a document
was translated. Structural validation prevents malformed translated XML, moved anchors, or altered
formula identities from corrupting the document.

New archives use PCEX format v4. PDF Craft continues to read v1, v2, and v3 archives. The PDF
renderer does not select a stored translation layer; translate_pdf() retains its direct
translation-and-patch workflow.

See #486,
#487,
#488,
#489,
#490, and
#494.

More complete and reliable translated output

  • ChapterXMLTransformer now includes image and table title, content, and caption fields in the
    same PCEX translation layer. Optional page-furniture translation is stored with that layer too.
  • Translated EPUB files keep their table of contents, XHTML titles, book metadata, NCX title,
    language, and archive contents consistent across single- and multi-chapter books.
  • PCEX rendering preserves inline formulas, complete headings, and anchored image/table structure
    in replacement and bilingual output.
  • PDF patching copies PCEX book metadata into standard PDF Info fields and preserves the complete
    normalized metadata in /PDFCraftDocumentMetadata.
  • Translation preserves source text that is already in the requested target language, and PDF
    patching supports scanned pages stored as full-page Stamp appearances.

See #477.

Upgrade checklist

  • Use AsyncPDFCraft inside an event loop. The synchronous PDFCraft facade rejects calls
    from a thread with a running event loop.
  • Replace direct PDFCraftExtraction.open() and .export() calls with the corresponding
    methods on PDFCraft or AsyncPDFCraft.
  • Replace ExtractionOptions.includes_footnotes with footnotes=FootnoteOptions(), or configure
    FootnoteRefinement to enable the new JEV-assisted tier.
  • Provide an explicit AsyncExecutor to Vendor OCR, XML translation, and JEV/LLM refinement.
    Share an executor only when those operations should share one capacity limit.
  • Provide target_language when the translator does not declare it. PCEX translation fails
    before making model calls when the target language is unknown.
  • translate_extraction() now writes a translation layer while preserving the source.
    render_markdown() and render_epub() default to RenderMode.SOURCE; select
    RenderMode.REPLACE or RenderMode.BILINGUAL to render a stored translation.

Other project updates

Localized READMEs now live under docs/readmes/, documentation navigation has been repaired,
and project links use pdfcraft.ai.

See #476 and
#479.

Full Changelog: v2.3.1...v2.4.0