Create, inspect, edit, redact, sign, render, and verify PDF files through explicit, fail-closed provider routes. Use for new tagged documents, imported PDFs, forms, annotations, OCR, conformance, and visual QA.From its SKILL.md
npx -y skills add w31r4/OfficeKit --skill pdfAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
15.5 KB, as published. Nobody here has run it
PDF Skill
Use ../office-kit/references/workspace.md for inputRoot, taskRoot, outputRoot, evidenceRoot, absolute PDF paths, SHA-256, locators, and visual-review status.
For multi-step PDF work use officekit repl and ../office-kit/references/repl.md; import through ctx.import, keep summaries in ctx.state, publish with ctx.publish, register QA with ctx.recordEvidence, and inspect before retrying maybeApplied mutations. Provider setup, OCR, signing, repair, and conformance remain explicit commands.
After the final PDF is reopened, follow ../office-kit/references/review.md.
The optional AnyDoc text reading view (contentView: "anydoc") is bounded, not OCR, signature/redaction evidence, PDF/UA validation, or visual QA.
Use the public office-kit package for OfficeKit PDF work. Never import or use @oai/artifact-tool: it is a different host runtime, not an OfficeKit alias or fallback, and its output must never be attributed to OfficeKit.
This Skill gives an agent bounded, auditable PDF primitives. PDF is independent
from the OfficeKit DOCX/XLSX/PPTX codec: do not add a PDF protobuf/WASM codec or
reconstruct an imported PDF through PdfArtifact or PDF.js and call that a
fidelity-preserving edit.
PdfArtifact is for new semantic/tagged documents. PdfFile plus required,
runtime-lazy MuPDF.js is the default for arbitrary PDF inspect, render, and bounded direct-original edits. Every specialist route is explicit. A failed
provider is an error, never a silent fallback.
Use createPdfjsParser() only as an optional read-only PDF.js adapter for page geometry, positioned text, heuristic tables, and bounded image evidence; it is never an imported-PDF edit representation.
Route every job first
Use this sequence for every request:
- Preserve original bytes and inspect with MuPDF.js.
- Resolve the declared intent with
PdfProviders.resolve. - If the result is
installable, obtain explicit project-policy authority and callensure; otherwise use only the returnedreadyprovider. - Probe the selected provider, perform one explicit save strategy, then inspect, audit, render, and review the output.
import { PdfFile } from "office-kit";
import { PdfProviders } from "office-kit/pdf/providers";
const inspection = await PdfFile.inspectPdf("input.pdf");
let resolution = await PdfProviders.resolve({
task: "edit-content",
inspection,
savePolicy: "rewrite",
mutationAuthorized: true,
});
if (resolution.status === "installable") {
resolution = await PdfProviders.ensure({ resolution });
}
if (resolution.status !== "ready") throw new Error(resolution.reason.message);
The public resolver returns only ready, installable, or blocked, with the
selected provider/pack, platform, pinned version/artifact information, download
and unpack estimates, licence acknowledgement, runtime paths, prerequisites, and operation boundary. It never selects an alternate provider.
PdfProviders.ensure({ resolution }) never acquires P12/private keys,
HSM/remote-signing credentials, TSA/LTV access, or other secrets.
PdfProviders.probe({ provider }) checks exactly that provider without a
download or fallback.
Run the default native CLI before an imported-PDF operation:
officekit run scripts/mupdf.mjs probe
officekit run scripts/mupdf.mjs inspect input.pdf
officekit run scripts/mupdf.mjs render input.pdf tmp/pdfs/page-1.png --page 1 --dpi 144
The CLI budgets work, refuses overwrite/symlink aliases, writes atomically, and never falls back. Source-bound outline edits are in edit existing.
Capability-pack policy
The only required npm runtime is [email protected]; it initializes only on the
first MuPDF-backed PDF operation. Root import and provider resolution do not
start WASM, download a pack, or modify the filesystem.
External capability packs live in the project-private
.office-kit/providers/ cache. The conventional policy is
.office-kit/pdf-providers.json; a missing file means
installPolicy: "disabled". An agent may install only after the user/project
sets managed, whitelists every provider and pack, accepts required licences,
allows the requested OCR languages, and supplies finite byte budgets:
{
"installPolicy": "managed",
"allowedProviders": ["qpdf"],
"allowedPacks": ["qpdf"],
"acceptedLicenses": [],
"allowedOcrLanguages": ["eng", "chi_sim"],
"maxDownloadBytes": 250000000,
"maxUnpackedBytes": 750000000
}
Only hash-pinned, versioned project release assets may be installed; an
enterprise mirror must serve identical bytes. The installer uses a lock,
temporary download, exact size/hash checks, safe archive extraction, atomic
publication, and a receipt. It rejects latest, package-manager/global-pip
installs, lifecycle hooks, path traversal, links, and undeclared URLs. The
managed targets are darwin-arm64, linux-x64, and win32-x64.
Current release-catalog state: qpdf 12.3.2-oat.2, python-foundation/python-specialists 3.13.14-oat.2, veraPDF/JRE 1.30.2-oat.2, OCR core 17.8.1-oat.3, and eng/chi_sim 4.1.0-oat.3 have published, attested darwin-arm64, linux-x64, and win32-x64 assets; public live acceptance verifies those all-platform closures. Poppler QA 24.08.0-oat.2 is also published and attested for all three managed platforms; macOS/Linux use source-built relocatable closures with pinned Poppler data and declared native roots, while Windows retains its reviewed upstream closure.
qpdf covers repair, linearization, inspection, and the bounded AES-256 delivery-copy route. The foundation is isolated CPython with ReportLab,
pdfplumber, pypdf, and Pillow. Specialists contain PyMuPDF, pikepdf, pyHanko,
and certificate validation; they depend on qpdf and require the catalogued
AGPL-or-commercial acknowledgement. The veraPDF pack carries its managed JRE;
OCR installs qpdf, its isolated OCRmyPDF/Tesseract 5/Ghostscript/pdftotext core, and only explicit language packs. Poppler QA 24.08.0-oat.2 is published and attested for darwin-arm64, linux-x64, and win32-x64; a policy-authorized route on any catalogued platform resolves as installable and uses its pinned pdfinfo, pdftoppm, and pdftotext bytes. The dedicated live release check uses resolve → ensure → probe, native text extraction, and raster output against those exact bytes. Use a selected system-only policy with an explicitly configured local runtime when one is already managed by the deployment. Once a task chooses managed or system-only, do not switch routes.
See provider setup and probes for the full policy, system-runtime, probe, and failure contract. The provider matrix states human-readable capability boundaries; the public catalog is the only source of pack versions, hashes, sizes, and installation facts.
Choose the narrowest provider
| Need | Explicit route | Detailed task |
|---|---|---|
| New tagged/semantic PDF | PdfArtifact | create |
| New layout-oriented PDF | ReportLab | create |
| Imported-PDF inspect/render/bounded edit | MuPDF.js / scripts/mupdf.mjs | read, edit |
| Text/table geometry evidence | pdfplumber | read |
| Attachments, complex forms, verified static-form delivery, merge/reorder/stamp | pypdf | forms, transform |
| Strict scrub, residue/OCR redaction, advanced bounded edit | PyMuPDF | redact |
| Repair or linearize | scripts/qpdf_provider.py | repair |
| Create an AES-256 encrypted delivery copy | scripts/qpdf_provider.py | encryption |
| Active/auxiliary structure cleanup | scripts/pikepdf_provider.py | structure cleanup |
| Searchable-layer OCR | scripts/ocrmypdf_provider.py | OCR |
| Local PKCS#12 sign, validation, or controlled P=2 form finalisation | scripts/pyhanko_sign_provider.py, scripts/pyhanko_provider.py, scripts/pyhanko_certified_form_fill.py | sign |
| PDF/A or PDF/UA machine checks | scripts/verapdf_provider.py | accessibility |
| Independent native visual QA | Poppler | render review |
Imported-PDF invariants
Keep the source immutable, bind its SHA-256, choose read-only, rewrite,
incremental, or sanitize before mutation, and publish a distinct output.
Inspect signatures, ByteRange, DocMDP/FieldMDP, encryption, forms, annotations,
attachments, metadata, active content, page boxes, and page count first.
incremental preserves old bytes; it is not signature authorization.
MuPDF.js supports only bounded direct-original operations. Its inspect output
keeps raw mediaBox/cropBox as unrotated PDF-space facts and emits a rotated
effective mupdf-page-space bbox for 0/90/180/270-degree placement. Use its
returned sourceSha256, mupdf-link/annotation/form locators,
snapshots/capabilities, and appearanceBbox; re-inspect after every output.
add_text_annotation,add_free_text_annotation,add_area_annotation,add_text_markup(Highlight/Underline/StrikeOut/Squiggly), compatibilityadd_text_highlight,add_link,delete_annotation,update_annotation,delete_link,update_link, andupdate_form_fieldare source-bound operations; placement uses the inspected page snapshot.add_text_annotationtakes a visible pin;add_free_text_annotationtakes a fixed-Helvetica visible text box and refuses an appearance that omits text;add_area_annotationadds one unfilled solid rectangle/ellipse whose full native appearance stays visible. All require rewrite. Recognized FreeText permits content/reviewer updates, and a recognized solid/no-fill area mark permits content/reviewer/RGB-stroke updates, each with a complete snapshot; text markup requires one unique native text selection.- On untagged input,
delete_page,duplicate_page, and changedrearrange_pagesrequire exact source SHA-256 and page snapshots, run as the only operation in a full rewrite, then require re-inspect and mapped Poppler render/pixel identity. delete_embedded_filebinds one inspect-returned canonical catalog NameTree locator and complete snapshot; rewrite removes that entry only and never claims sanitize or physical payload erasure.set_metadatabinds the exact source plus the completemupdfDocumentMetadatasnapshot and updates standard Document Info fields. For boundedfield-safe-v1XMP, inspection reports separatexmpMutableFieldsandxmpBlockedFields; the same transaction updates only requested proven fields and leaves all other packet bytes unchanged. A uniquex-defaultremains editable in a multilingual title/description, common scalar description attributes are editable, and an unrelated multi-author or irregular field blocks only that field. Missing or blocked requested fields, CDATA/DTD/invalid entities, malformed XML, stale/no-op evidence, and unsupported stream graphs fail closed without switching providers.set_page_cropis raw unrotated CropBox visibility only, not redaction.rotate_pagesets an absolute right-angle/Rotate; neither enables content reflow. Delete/redaction operations cannot be incremental. Mixed-document OCR needing automatic rotate or deskew is audit-onlyfailed_closedwithsavePolicy.strategy: "none"; see OCR.- General Word-style reflow, arbitrary text replacement, Dynamic XFA, complex
JavaScript, 3D, and RichMedia are not made safe by these primitives. Current
typed routes cannot prove opaque closure or runtime behavior, so inventory
such objects and fail closed with audit-only
save: noneunless a dedicated provider and oracle prove unchanged bytes plus runtime semantics. Never route around this boundary through pypdf, ReportLab, PDF.js, content-stream edits, or another provider; do not publish a cover-only edit as preserved behavior.
For the complete operation schemas and edge cases, read edit existing and forms and annotations, not this overview.
Specialist safety boundaries
- qpdf receives a source SHA-256 before repair or linearize. Its separate
encryptprimitive creates one AES-256 copy from an unencrypted source using caller-owned restricted password files and private argument files; it does not open/decrypt/re-encrypt existing encrypted PDFs or edit permissions. Both routes are structural/full-rewrite operations, not redaction or sanitize; use repair or encryption. - pikepdf offers only
active-contentandactive-and-auxiliaryprofiles. It is not redaction, metadata cleanup, or XFA cleanup; use structure cleanup. - PyMuPDF
redact_ocr_textis sanitize-only: require exactexpected_rotation(0/90/180/270), a named language, match count, bounded raster work, residue evidence, rewrite, and invalidation acknowledgement. Coordinates remain unrotated PyMuPDF page space. A complete imported PDF OCR workflow is not a sanitizer; see OCR. - pyHanko local PKCS#12 signing uses a passphrase on stdin only. Its P=2 route
finalises one verified empty decimal field under an exact FieldMDP lock set;
pyhanko_provider.pyvalidates under an explicit trust root. Timestamp, LTV, PKCS#11/HSM, remote signing, and network evidence are external workflows. - veraPDF's
verapdf_provider.pyis a machine-rule gate, not repair or a substitute for human review of PDF/UA. See accessibility.
When a Python specialist is selected, one configured virtual environment
executable remains provider identity so its pyvenv.cfg is preserved. Do not
retry via a different Python interpreter after a failed probe.
Delivery gate
- Record selected provider, version, policy/receipt (if any), source hash, save strategy, and no-fallback evidence in the canonical audit envelope. A no-mutation refusal uses
pdf_audit.py failed-closed; a verified DocMDP P=1 refusal additionally supplies its pyHanko report with--signature-verification --require-docmdp-no-changes --trust-root. Do not hand-write either audit. A refused PAdES/TSA/LTV upgrade must pass--capabilities-jsonwith explicit booleanfalsevalues fortimestampAuthoritySupported,ltvEmbeddingSupported, andpadesProfileConformanceClaimed; prose or missing fields are not evidence. - Reopen and verify the intended change; use
scripts/pdf_audit.py validate. - Run the requested specialist evidence. Sanitization must pass its residue and single-revision gates; signatures and PDF/UA retain their separate validation/human boundaries.
- Render every final page with MuPDF.js or independently with Poppler and inspect clipping, overlaps, glyphs, images, fields, annotations, signatures, redactions, and page geometry when visual input is available. Without it, retain renders, run structural/page-geometry checks, and mark visually material results for human review. Request AnyDoc only to close a text/table coverage gap; it is not OCR.
The project and its required MuPDF.js dependency are GNU AGPL-3.0-or-later. Managed and system providers retain their own licences; the resolver exposes the acknowledgement required before any installation or operation.