Guide
How to use the APIs paper-pptx adds, grouped as perceive, edit, compose, and verify, plus the error taxonomy.
This page summarizes the APIs paper-pptx adds to python-pptx. The generated API reference covers the complete public API, and the comparison page describes deliberate behavior changes at package boundaries.
Safety contract
Paper's guarded mutating APIs either complete or refuse atomically. An
operation that cannot proceed safely raises a typed refusal and restores the
presentation's package state. Programmer mistakes (a bad type, an out-of-range
index) raise plain ValueError or TypeError.
The refusal hierarchy in pptx.errors:
PaperRefusal, the base classPackageLimitError: archive has no unambiguous reading or is unsafe to expandAmbiguousTargetError: addressing matches more than one targetTargetNotFoundError: addressing matches nothingStaleAnchorError(subclass ofTargetNotFoundError): the block at an anchor no longer matches its content fingerprintUnsupportedStructureError: structure the API cannot operate on safelyBoundaryViolationError: the operation would cross a boundary it promised to stay insideRelationshipPolicyError: the relationship graph cannot be honored under the requested policy
Perceive: pptx.inspect
Read-only resolution of what a deck renders, through the placeholder,
layout, master, and theme inheritance walk. Each value reports its sources.
Unsupported values (gradients, East Asian typefaces, style modulations)
report resolved=False.
effective_font(run) -> EffectiveFont # also run.effective_font()
effective_paragraph_format(paragraph) -> EffectiveParagraphFormat
effective_shape_format(shape) -> EffectiveShapeFormat
inspect_text(slide) -> TextInspection
inspect_deck(prs) -> DeckManifest
content_hash(text) -> strrun = prs.slides[0].shapes.title.text_frame.paragraphs[0].runs[0]
font = run.effective_font()
if font.size.resolved:
print(font.size.value_pt) # e.g. 36.0
for step in font.size.provenance:
if step.supplied:
print("supplied by", step.level) # e.g. "master txStyles titleStyle lvl1"Each EffectiveValue carries value, value_pt, resolved, and the
ordered provenance chain. inspect_text() traverses top-level shapes,
groups recursively to any depth, and table cells in a pinned order. Every
TextBlock carries a structural BlockAnchor with a full content fingerprint
for stale-safe edits. Regions the library cannot read are counted as blind
regions. inspect_deck() returns a deterministic, JSON-friendly
DeckManifest (slides, shapes, z-order, geometry, placeholder bindings,
autofit state, tables, charts, images, masters).
Edit: pptx.edit and inherited-class methods
Text replacement
replace_text(prs, find, replace, *, include_notes=False) -> ReplaceResult
replace_text_at(prs, anchor, find, replace) -> ReplaceResult
refind(prs, anchor) -> BlockAnchorDeck-wide and formatting-preserving. Matches do not cross paragraph,
line-break, or field boundaries. Replacement text inherits the formatting of
the run where the match starts. The full traversal is materialized before
the first write, so a refusal leaves the deck unchanged. replace_text_at
locates the structural container and then verifies its content fingerprint.
It raises StaleAnchorError when that content is stale; refind is the
recovery path.
Slide lifecycle (Slides)
prs.slides.clone(source, *, after=None, policy=None) -> Slide
prs.slides.delete(slide) -> None
prs.slides.move(slide, to_index) -> None
prs.slides.reorder(new_order) -> None # exact permutation requiredclone is a policy-governed deep copy: charts are deep-copied with their
embedded workbooks and style parts, notes are deep-copied and re-linked, and
media is shared (tunable via SlideClonePolicy). Relationship types that
cannot be cloned safely (OLE objects, ActiveX controls) raise
RelationshipPolicyError before anything changes. delete purges section
and custom-show references and does not strand orphaned parts.
Bullets, autofit, notes
paragraph.bullet.set_character("•", font_name=None, size_percent=None)
paragraph.bullet.set_numbered("arabicPeriod", start_at=1)
paragraph.bullet.set_none()
text_frame.font_scale # e.g. 62.5 (normAutofit), else None
text_frame.line_space_reduction # e.g. 20.0
text_frame.normalize_autofit(min_font_size=None, resolve=False)
slide.read_notes_text() -> str # does not create a notes part
slide.replace_notes_text(text) # edits only an existing notes bodyBullets write a:buChar, a:buAutoNum, or a:buNone markup with
hanging-indent geometry. normalize_autofit() bakes the rendered sizes into
explicit formatting before disabling autofit. A run whose size cannot be
resolved refuses unless resolve=True walks the effective-style chain, and
the walk refuses what it cannot resolve.
Shape and table operations
shapes.shape_by_name(name); shapes.picture_by_name(name)
shapes.table_by_name(name); shapes.chart_by_name(name)
shapes.add_copy(shape) -> Shape
shapes.delete(shape); shapes.move(shape, to_index)
table.insert_row(after, *, copy_format_from=None) -> _Row
table.insert_column(after, *, width=None, copy_format_from=None) -> _Column
table.delete_row(row_idx); table.delete_column(col_idx)
cell.extend_merge(other_cell) -> None
picture.replace_image(image_file, *, allow_format_change=False)
chart.replace_data_safe(categories, series, *, number_format=None)The *_by_name lookups recurse into groups, return the right object type,
and raise AmbiguousTargetError on duplicate names. Table operations guard
merged regions cell by cell; an operation refuses only when it would split a
merge. replace_image() swaps the image relationship and leaves position,
size, rotation, and crop untouched; when two pictures share one image part,
only the target changes. replace_data_safe() validates the chart and
workbook structure before writing, refuses shared chart parts and
unsupported chart families (XY, bubble, stock, surface, radar, 3-D,
multi-plot combos), and supports workbook-less charts.
Compose: pptx.compose, pptx.rebind, pptx.hf
Cross-deck import
prs.import_slide(source_prs, slide, *, mode, position=None,
notes=True, section=None, section_id=None,
target_layout=None, placeholder_map="auto") -> ImportReport
prs.append_deck(source_prs, *, mode, notes=True) -> tuple[ImportReport, ...]mode is required and has no default:
"adopt_theme": rebind to a destination layout. Orphaned placeholders bake from their source-resolved look, andrun_shiftsreports each appearance change."keep_appearance": transplant the source layout, master, and theme chain, fingerprint-deduplicated, so ten slides from one source share one master."bake": freeze resolved formatting into the slide.
The source deck is never mutated (byte-tested). Media copies; charts
deep-copy with workbooks; comments drop and the report says so. Unsafe
relationship types raise RelationshipPolicyError before the first write.
Automatic layout and placeholder matches must be unique. Use target_layout
or a partial placeholder_map to resolve ambiguity. section selects a
unique exact name; section_id selects an exact stored GUID.
report = prs.import_slide(source, 0, mode="adopt_theme")
for shift in report.run_shifts:
print(shift.text, shift.before["name"]["value"], "->", shift.after["name"]["value"])Layout rebind
slide.rebind_layout(target_layout, *, placeholder_map="auto",
orphan_policy="refuse") -> RebindReportMoves a slide to another layout in the same package under explicit placeholder and orphan policies. It runs the effective-value resolver before and after, and reports every run whose resolved appearance changed.
Footers and fields
prs.apply_footers(*, footer=None, slide_number=False, date_format=None,
fixed_date=None, skip_title_slides=False, now=None) -> None
slide.apply_footers(...) # per-slide variantReproduces PowerPoint's Insert → Header & Footer behavior with a:fld
slide-number and date fields that renumber on reorder, bound to the layout's
footer placeholders, applied idempotently. date_format takes the ISO/IEC
29500 tokens ("datetime1" through "datetime13"). fixed_date writes a
literal string. Passing both raises ValueError.
Verify: pptx.diff and pptx.package
diff_decks(path_a, path_b, *, detail="structure") -> DeckDiffSlides are matched by permanent slide ID, so a reorder reports as a move
rather than a delete plus an add. Top-level shapes match by shape ID and
compatible kind. detail levels: "structure" (slide add/remove/move, shape
add/remove, geometry, table dimensions, image replacement), "text" (adds
canonical text-region, chart-data, and notes deltas), "full" (adds per-run
effective-value and bullet shifts; expensive). ID matching serves decks
derived from a common ancestor; independently authored decks are out of scope.
xml_equivalent(a, b) -> bool
diff_package(path_a, path_b) -> PackageDiff
patch_save(original_path, document, out_path) -> PackageDiffxml_equivalent compares canonicalized XML and treats meaningful
whitespace, including a trailing space inside a run, as content.
patch_save serializes the deck, then restores the original bytes of every
semantically identical member: a one-line edit to a sixty-slide deck diffs
as one part, a no-op round trip is byte-identical, entry order and
timestamps are deterministic, and the write is atomic.
Report objects
These public operations return typed, versioned, JSON-friendly reports:
| Object | Returned by | Schema |
|---|---|---|
TextInspection | inspect_text | paper-text-inspection v3 |
DeckManifest | inspect_deck | paper-deck-manifest v1 |
EffectiveFont | effective_font | paper-effective-font v2 |
ReplaceResult | replace_text, replace_text_at | paper-replace-result v2 |
ImportReport | import_slide, append_deck | paper-import-report v3 |
RebindReport | rebind_layout | paper-rebind-report v1 |
DeckDiff | diff_decks | paper-deck-diff v5 |
PackageDiff | diff_package, patch_save | paper-package-diff v1 |