- Cross-paragraph reflow is not supported — text reflows within a single paragraph only
- Mixed-font paragraphs (e.g., bold words within regular text) lose inline formatting during reflow
- Text inside Form XObjects (reusable content streams) is not found or edited
- Justified text may lose justification after reflow (replaced with left-aligned)
- Empty string replacement removes the text visually but leaves the operator structure intact
- Rotated text (non-zero CTM rotation) is extracted correctly and
find()returns matches for small angles (tested up to 5 degrees). Replacement positioning under rotation has not been extensively tested. - Horizontally scaled text (CTM Tz) is found and replaceable — the engine correctly handles width changes.
- Very small text (6pt) is found and replaceable.
- Character spacing (
Tcoperator) is handled correctly — text is stored as a single string in the content stream, not as individual characters with spaces.
- PDFs previously edited by PyMuPDF (redact + re-insert) can be read, searched, and edited by pdf-edit-engine. The mixed font origins (original + PyMuPDF-added) do not cause issues.
-
Tier 1.5 font extension (in-place glyph injection) requires the matching system font (or a metric-equivalent fallback like Carlito for Calibri) to be installed; the resolved substitute name is surfaced through
FidelityReport.font_substitutedAND afont_coverage_substitutedDegradation (v0.1.3) -
CID-keyed (Type0) CFF / Type1C embedded fonts ARE extended in place as of v0.2.0 (C.3 —
_inject_cff_glyph_in_place, the Type2-charstring sibling of the TrueTypeglyfinjector; collision-freeCID == GID, pre-existing CIDs preserved). Out-of-scope CFF shapes (simple-font/non-CID CFF, CFF2, name-keyed CFF, multi-FD CID, composite donors,unitsPerEmmismatch) refuse honestly via afont_extension_failedDegradation (success=False) -
CJK fonts with 30,000+ glyphs have not been tested
-
Type 3 fonts (bitmap/procedural) are not supported for extension
-
Emoji and other multi-codepoint characters cannot be rendered if the font lacks those glyphs (reported via
FidelityReport.degradationsasfont_extension_failed) -
Non-CID font extension is supported for simple TrueType + WinAnsi +
/FontFile2fonts (Phase 13). Replacements requiring missing glyphs trigger_extend_simple_tier_15, which sources the outline from a system font (or metric-equivalent fallback like Carlito for Calibri) and surfaces viaFidelityReport.font_substitutedAND afont_coverage_substitutedDegradation. Still NOT supported on the simple-font path:/FontFile3(CFF/OpenType) outlines — note that CID-keyed (Type0) CFF/Type1C IS injected as of v0.2.0 (C.3); this remaining gap is the non-CID simple-font case only;/Type1fonts (would require Type1 charstring surgery — out of scope); MacRoman simple fonts (architecturally similar to WinAnsi but untested); CJK fonts with 30,000+ glyphs (untested). The dispatcher inextend_subset()raisesFontNotFoundErrorfor all these cases.Most public-API entry points surface failure modes as typed
Degradationentries onEditResult.fidelity_report.degradations. Coverage is enforced for the documented kinds via INV-J-9 (added in v0.1.3, commit 01:font_action="failed"implies aDegradationof kind inFONT_AFFECTING_KINDS). Other failure modes (e.g., reflow abort, glyph-coverage gaps) are surfaced opportunistically and may not always carry a typed entry. Seedocs/v0.1.3-release-notes.md§Honesty fixes for the full coverage matrix.
- Identity-H CIDFont is the primary encoding path; WinAnsi is fully supported
- Complex color spaces (ICC profiles, Separation, DeviceN) are tracked but not fully resolved
- Custom encodings with unusual /Differences arrays may fail
- MacRoman and other legacy encodings are less tested
Benchmarks on a typical developer machine (Windows 11, Python 3.12, WinAnsi PDFs):
| Operation | Input | Time |
|---|---|---|
get_text() |
100-page PDF | ~0.3s |
find() |
100-page PDF (900 matches) | ~0.3s |
replace() |
Single page | ~0.03s |
batch_replace() |
50 edits | ~0.1s |
Identity-H CIDFont PDFs (Chrome, Google Docs, Word) may be slower due to CMap parsing and width lookups. Performance scales linearly with page count.
- Encrypted PDFs require the password to open, but once it is provided they are edited and re-saved with their encryption preserved as of v0.2.0 (A2.3 — re-encrypted with the same algorithm and permissions; a genuine re-encryption failure falls back to an unencrypted save and surfaces an
encryption_droppedDegradation) - PDF/A compliance is not maintained after editing
- Digital signatures are invalidated by any edit (inherent to how PDF signatures work)
- Linearization (Fast Web View) is preserved on save as of v0.2.0 (A2.2 — a linearized input is re-linearized; on the rare case pikepdf cannot re-linearize, the edit still succeeds via a normal save and surfaces a
linearization_droppedDegradation). A non-linearized input is unaffected - Right-to-left text is not corrupted but reflow does not handle RTL properly
- XFA forms are not supported
- Content streams with non-UTF-8 byte sequences in operators are rejected with a clear error
- The library is not thread-safe. Caches (
FontResolverCache,GlyphWidthCache) and the publicpikepdf.Pdfhandle returned by_pathutil.open_pdfare designed for single-threaded use. As of v0.1.2 (ARY-283) every public entrypoint constructs fresh per-call caches — there is no shared module-level state that two threads could race on inside the engine — but the underlyingpikepdf.Pdfobject, the page-level mutations performed bysurgeonandstructural, and thefontTools.ttLib.TTFontinstances loaded insidefonts.extend_subsetare not safe to share across threads. - Recommended pattern: one engine call per thread, each operating
on its own
pikepdf.Pdfhandle (i.e. its own input path or its ownopen_pdf(...)context). Do not pass the samePdf, page, or font resolver between threads. - For server-side concurrent processing, scale by process (one worker per request) rather than by thread. The library's per-call cost (single-document edits in tens of milliseconds; see Performance table above) makes process-level concurrency cheap.
- Concurrency depth is not formally probed (F-B-07). v0.1.3 does
not exercise
replace_block/batch_replace_blockunder concurrent multi-process load. Single-process behavior is well-tested; multi- process callers writing the same file should serialize via OS-level file locks or a queue. Tracked for a future release.
- No engine-side timeout on edit operations (F-D-DD). A pathological
PDF (corrupt CMap, deep composite glyph graph, very large embedded
subset) can stall an edit for minutes inside
replace_blockreflow orextend_subset. Callers running the engine in a request-response context should impose their own timeout (e.g.concurrent.futures.ProcessPoolExecutorwith a per-task deadline, or a watchdog process). The composite-depth cap (v0.1.3,MAX_COMPOSITE_DEPTH=64), the graphics-stateq/Qdepth cap (v0.2.0,MAX_GRAPHICS_STATE_DEPTH=128), and the font/CMap Flate decompression-bomb guard (v0.2.0, 32 MiB / 8 MiB decoded caps) close the deepest known classes of pathology; an engine-side wall-clock timeout remains tracked for a future release.