Repository navigation
v2.4.0
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
FootnoteRefinementfor 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,
)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.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
ChapterXMLTransformernow 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
AsyncPDFCraftinside an event loop. The synchronousPDFCraftfacade rejects calls
from a thread with a running event loop. - Replace direct
PDFCraftExtraction.open()and.export()calls with the corresponding
methods onPDFCraftorAsyncPDFCraft. - Replace
ExtractionOptions.includes_footnoteswithfootnotes=FootnoteOptions(), or configure
FootnoteRefinementto enable the new JEV-assisted tier. - Provide an explicit
AsyncExecutorto Vendor OCR, XML translation, and JEV/LLM refinement.
Share an executor only when those operations should share one capacity limit. - Provide
target_languagewhen 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()andrender_epub()default toRenderMode.SOURCE; select
RenderMode.REPLACEorRenderMode.BILINGUALto 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.
Full Changelog: v2.3.1...v2.4.0