paper-pptx
vs python-pptx
High-level changes relative to python-pptx 1.0.2, and why.
paper-pptx is a fork of python-pptx 1.0.2. It keeps the pptx import name and
upstream object model while adding guarded APIs for inspection, editing,
composition, and verification. Upstream's pytest and behave suites run on
every change.
High-level changes
- Perceive.
effective_font()and related functions resolve the values a deck renders through the placeholder, layout, master, and theme chain, and report the source of each value.inspect_text()reads into groups and table cells with structural anchors;inspect_deck()emits a deterministic manifest. - Edit. Anchored, formatting-preserving text replacement. Relationship-safe slide clone, delete, move, and reorder. Real bullet markup, autofit normalization, merge-aware table operations, and scoped image and chart-data replacement.
- Compose. Cross-deck slide import and deck append under explicit
fidelity modes (
adopt_theme,keep_appearance,bake). Layout rebind with a report of every appearance change. Footer, date, and slide-number fields written as reala:fldelements. - Verify.
diff_decks()matches slides by permanent ID, so a reorder reports as a move.patch_save()writes byte-minimal saves, so a one-line edit to a sixty-slide deck diffs as one part. - Guarded package intake. Opening a
.pptxrejects ambiguous or unsafe ZIP archives — duplicate or case-colliding members, noncanonical paths, encryption, unsupported compression — with aPackageLimitErrorbefore any editable object exists. - Atomic save. Path saves write a sibling temp file and replace the destination after serialization succeeds, preserving permission bits.
- Distribution identity.
import pptxfails when both distributions are installed.pptx.__version__stays"1.0.2"andpptx.__paper_version__identifies the fork. Python 3.9+ floor.
At a glance
| Capability | python-pptx | paper-pptx |
|---|---|---|
| Create slides, shapes, charts, tables | ✅ | ✅ (inherited) |
| Resolve rendered font/color through the theme chain | returns None | effective_font() with sources |
| See text in nested groups and table cells | manual recursion | inspect_text(), blind regions counted |
| Replace text preserving run formatting | shape.text overwrites | replace_text() / replace_text_at() |
| Clone / delete / move / reorder slides | — | Slides.clone/delete/move/reorder, relationship-safe |
| Real bullets | — | paragraph.bullet |
| Autofit made explicit | invisible scale % | font_scale, normalize_autofit() |
| Read notes without side effects | reading creates a part | read_notes_text() |
| Shape lookup by name | first match wins | *_by_name, ambiguity refuses |
| Merge-aware table row/column operations | — | insert_row/column, delete_row/column |
| Isolated image / chart-data replacement | shared parts contaminated | replace_image(), replace_data_safe() |
| Cross-deck slide import | — | import_slide(mode=...), append_deck() |
| Layout rebind with a shift report | — | rebind_layout() |
| Footer/date/slide-number fields | — | apply_footers() |
| Semantic deck diff | — | diff_decks() |
| Byte-minimal save | full rewrite every save | patch_save() |
| Untrusted-package preflight | straight to zipfile | typed PackageLimitError before parsing |
| Atomic save | in-place write | sibling temp file, then replace |
| Typed, catchable errors | — | pptx.errors.PaperRefusal hierarchy |
| Install verification | — | paper-pptx-doctor |