A condensed view of every public function in exis_pdfeditor (v4.5.8+).
For full parameter docs see the PyPI page
or the package README.
import exis_pdfeditor
# Trial (14 days, no key required)
exis_pdfeditor.initialize()
# Licensed
exis_pdfeditor.initialize("XXXX-XXXX-XXXX-XXXX")
# Or via environment variable
# export EXIS_PDF_LICENSE_KEY=XXXX-XXXX-XXXX-XXXXinfo = exis_pdfeditor.inspect("file.pdf")
# -> info.version, info.pageCount, info.title, info.author, info.producer,
# info.isEncrypted, info.hasFormFields, info.formFieldCount,
# info.fontsUsed, info.pages[]
dump = exis_pdfeditor.dump_structure("file.pdf")
# -> dump.totalObjects, dump.streamObjectCount, dump.isEncrypted, ...
pages = exis_pdfeditor.analyze_pages("file.pdf")
# -> pages[].pageNumber, .kind (Digital|Scanned|AlreadyOcrd|Empty),
# .textCharCount, .textCoverageRatio, .imageCoverageRatio,
# .hasInvisibleTextLayerresult = exis_pdfeditor.extract_text("file.pdf")
# -> result.fullText, result.pages[].text
result = exis_pdfeditor.extract_text("file.pdf", pages=[1, 3])
structured = exis_pdfeditor.extract_text_structured("file.pdf")
# -> structured.pages[].textBlocks[].x, .y, .fontName, .fontSize, .text# Single
exis_pdfeditor.find_replace("in.pdf", "out.pdf", "old", "new")
# Multiple pairs
exis_pdfeditor.find_replace("in.pdf", "out.pdf", pairs=[
{"search": "{{NAME}}", "replace": "John"},
{"search": "{{DATE}}", "replace": "2026-04-07"},
])
# Regex
exis_pdfeditor.find_replace("in.pdf", "out.pdf", pairs=[
{"search": r"\d{3}-\d{2}-\d{4}", "replace": "[SSN]", "isRegex": True},
])
# Multi-line wrap (\\n in replace string when wrap="explicit")
exis_pdfeditor.find_replace(
"in.pdf", "out.pdf",
"Short label", "Longer label that\nwraps to two lines",
wrap="explicit", # auto | explicit | off
max_lines=3,
line_spacing=1.2,
)
# All options
exis_pdfeditor.find_replace(
"in.pdf", "out.pdf",
"find", "replace",
case_sensitive=False,
whole_word=True,
use_regex=False,
page_range=[1, 2, 3],
text_fitting="adaptive", # none | preserve_width | fit_to_page | adaptive
min_horizontal_scale=70,
max_font_size_reduction=1.5,
replacement_text_color={"r": 1, "g": 0, "b": 0},
replacement_highlight_color={"r": 1, "g": 1, "b": 0},
replacement_bold=True,
replacement_underline=False,
replacement_strikethrough=False,
preserve_form_fields=True,
use_incremental_update=True,
wrap="auto",
max_lines=None,
line_spacing=None,
)exis_pdfeditor.merge(["a.pdf", "b.pdf", "c.pdf"], "merged.pdf")
# One file per page
result = exis_pdfeditor.split("doc.pdf", "out_dir/")
# -> result.pageCount, result.files[]
# Split by outline / TOC
result = exis_pdfeditor.split(
"book.pdf", "chapters/",
by_bookmarks=True,
prefix="ch_",
max_depth=1,
number_names=False,
)
# -> result.sectionCount, result.sections[].title, .startPage, .endPage, .file
toc = exis_pdfeditor.bookmarks("doc.pdf")
# -> toc.count, toc.bookmarks[].title, .level, .startPage
exis_pdfeditor.extract_pages("doc.pdf", "subset.pdf", pages=[1, 3, 5])fields = exis_pdfeditor.list_fields("form.pdf")
# -> field.name, .displayName, .type, .value, .options,
# .isReadOnly, .hasDuplicateWidgets
# Per-widget names for duplicate widgets (two-up / carbon copies)
fields = exis_pdfeditor.list_fields("form.pdf", split_duplicate_widgets=True)
# e.g. Address_1, Address_2
exis_pdfeditor.fill_form(
"form.pdf", "filled.pdf",
fields={"FirstName": "John", "Email": "j@example.com"},
flatten=True, # optional - lock values into page content
text_alignment="center", # auto | left | center | right
split_duplicate_widgets=False,
)
# Signature *image* fields (not PKCS#7 digital sign — see Signatures below)
sig_fields = exis_pdfeditor.list_signature_fields("contract.pdf")
exis_pdfeditor.fill_signature(
"contract.pdf", "signed_image.pdf",
image_path="signature.png",
field="Signature1",
scale="fit", # fit | stretch
halign="Center", # Left | Center | Right
valign="Middle", # Bottom | Middle | Top
padding=2.0,
flatten=True,
)exis_pdfeditor.redact("doc.pdf", "redacted.pdf", redactions=[
{"text": "John Doe", "replaceWith": "[NAME]"},
{"text": r"\d{3}-\d{2}-\d{4}", "isRegex": True, "replaceWith": "[SSN]"},
{"area": {"x": 100, "y": 700, "width": 200, "height": 20}, "pageNumber": 1},
])Erase content inside a rectangle, or paint an image/text over it.
exis_pdfeditor.erase_region(
"page.pdf", "erased.pdf",
page=1,
rect=(72, 400, 200, 40), # x, y, w, h (or dict / "x,y,w,h")
match="inside", # inside | intersects
keep_text=False,
keep_images=False,
keep_paths=False,
)
exis_pdfeditor.replace_region(
"page.pdf", "patched.pdf",
page=1,
rect={"x": 72, "y": 400, "width": 200, "height": 40},
image="logo.png",
image_scale="fit", # fit | stretch | natural
background="#FFFFFF",
text="ACME Corp",
font_size=12,
align="center", # left | center | right
valign="middle", # top | middle | bottom
)Use these when text/logos are drawn as paths (not selectable digital text).
find_replace will not match them.
# List outline clusters (blob recognizer — works in the Python wheel)
hits = exis_pdfeditor.find_vector_text("outlined.pdf", ocr="none")
# optional: pages=[1], language="en", render_scale=2.0
# Phrase replace needs a real OCR engine (Tesseract/Windows/Apple).
# The AOT CLI bundled in the wheel only ships ocr="none", which cannot
# match --find text. For production outlined-text replace, call the
# .NET API with Exis.PdfOcr, or expect the CLI to raise.
# exis_pdfeditor.replace_vector_text(
# "in.pdf", "out.pdf", find="ACME", replace="Contoso",
# ocr="tesseract", mode="outlines", align="auto",
# fit="shrink", wrap="auto",
# )
# Vector-drawn logos / icons
graphics = exis_pdfeditor.find_vector_graphics(
"branded.pdf",
thumbnails_dir="thumbs/", # writes graphic-{index}.png
min_size=8.0,
)
# -> graphics[].index, .pageNumber, bounds, ...
exis_pdfeditor.replace_vector_graphic(
"branded.pdf", "rebranded.pdf",
image_path="new-logo.png",
index=graphics[0].index, # int or list of indices
scale="fit", # fit | stretch | natural
)exis_pdfeditor.watermark("in.pdf", "out.pdf", "DRAFT")
exis_pdfeditor.watermark("in.pdf", "out.pdf", "CONFIDENTIAL",
position="across", # top | bottom | center | across
font_size=72,
text_color={"r": 1, "g": 0, "b": 0},
opacity=0.15,
page_range=[1, 2],
)
exis_pdfeditor.stamp("in.pdf", "out.pdf", "letterhead.pdf",
mode="overlay", # overlay | underlay
opacity=1.0,
page_range=[1],
)exis_pdfeditor.bates_stamp(
"in.pdf", "out.pdf",
prefix="ABC",
digits=6,
start_number=1,
# see README for confidentiality line, positions, page_range, etc.
)exis_pdfeditor.optimize("in.pdf", "out.pdf")
exis_pdfeditor.optimize("in.pdf", "out.pdf",
downsample_images=True,
max_image_dpi=150,
remove_metadata=True,
)exis_pdfeditor.encrypt("in.pdf", "out.pdf",
user_password="openme",
owner_password="secret",
permissions=["Print", "CopyText"],
# Available: Print, ModifyContents, CopyText, AddAnnotations,
# FillForms, PrintHighQuality, All
)
exis_pdfeditor.decrypt("in.pdf", "out.pdf", password="openme")exis_pdfeditor.rotate("in.pdf", "out.pdf", angle=90)
exis_pdfeditor.rotate("in.pdf", "out.pdf", angle=180, pages=[2, 4])
exis_pdfeditor.crop("in.pdf", "out.pdf",
rect={"x": 50, "y": 50, "width": 500, "height": 700},
)
exis_pdfeditor.reorder("in.pdf", "out.pdf", order=[3, 1, 2])
exis_pdfeditor.delete_pages("in.pdf", "out.pdf", pages=[2, 4])
exis_pdfeditor.insert_blank_pages(
"in.pdf", "out.pdf",
insertions=[{"afterPage": 1, "count": 2}],
)result = exis_pdfeditor.find_images("doc.pdf")
# -> result.totalImages, result.images[].index, .pixelWidth, .pixelHeight,
# .colorSpace, .format, .pageNumbers
# Save extracted images to disk
exis_pdfeditor.find_images("doc.pdf", output_dir="extracted/")
# Replace all images
exis_pdfeditor.replace_image("doc.pdf", "out.pdf", "new_logo.png")
# Replace specific images by index, scoped to a page
exis_pdfeditor.replace_image("doc.pdf", "out.pdf", "new_logo.jpg",
image_indices=[0, 2],
page_range=[1],
scale_mode="scale_to_fit",
# match_original_size | preserve_aspect_ratio | scale_to_fit
)exis_pdfeditor.sign("in.pdf", "out.pdf",
cert_path="cert.pfx",
cert_password="certpass",
reason="Approved",
location="New York, NY",
signer_name="John Doe",
)
# Visible signature box
exis_pdfeditor.sign("in.pdf", "out.pdf",
cert_path="cert.pfx",
cert_password="certpass",
visible=True,
page=1,
rect={"x": 50, "y": 50, "width": 200, "height": 60},
)
# Verify
sig = exis_pdfeditor.verify("signed.pdf")
# -> sig.isSigned, sig.signerName, sig.isValid, sig.reason, sig.signDate
# Multi-signature
all_sigs = exis_pdfeditor.verify("signed.pdf", all_signatures=True)result = exis_pdfeditor.pdfa_validate("doc.pdf", level="2b")
# Levels: 1b | 2b | 2u | 3b | 3u
# -> result.isCompliant, result.violations[].code, .message, .canAutoFix
exis_pdfeditor.pdfa_convert("doc.pdf", "archive.pdf", level="2b")meta = exis_pdfeditor.get_metadata("doc.pdf")
exis_pdfeditor.set_xmp("in.pdf", "out.pdf", xmp_path="meta.xmp")
exis_pdfeditor.set_info("in.pdf", "out.pdf", info={
"title": "Report",
"author": "Exis",
})
exis_pdfeditor.remove_xmp("in.pdf", "out.pdf")
exis_pdfeditor.remove_info("in.pdf", "out.pdf")
exis_pdfeditor.remove_metadata("in.pdf", "out.pdf") # bothRaises OcrNotSupportedError on Linux/macOS wheels.
exis_pdfeditor.make_searchable_pdf(
"scan.pdf", "searchable.pdf",
languages=["eng"],
dpi=300,
)
exis_pdfeditor.redact_scanned_pdf(
"scan.pdf", "redacted.pdf",
terms=["John Doe", "SSN"],
visible_replacement="[REDACTED]",
)| Tier | Behavior |
|---|---|
| Free (no init) | inspect(), analyze_pages(), pdfa_validate(), dump_structure() |
| Trial (init with no key) | All features, 14 days |
| Licensed (init with key) | All features, no expiry |
| Evaluation (after trial) | All features but limited to 3 pages per document |
Purchase: pdfbatcheditor.com/developers — $499 / developer / year.