Common issues and how to solve them.
pip install edof[all] failsMost likely one of the optional extras (PyQt6, pymupdf, reportlab, cryptography) doesn’t have a wheel for your Python version. Install with a smaller subset and add extras one by one to find the culprit:
pip install edof
pip install edof[crypto]
pip install edof[qr]
pip install edof[pdf]
pip install edof[pyqt6]
The one that fails is the one to investigate. Search for the error message in the failing package’s GitHub issues.
If a wheel doesn’t exist, you may need:
gcc on Linux; Xcode CLI tools on macOS)edof-editor not found in PATHThis means either:
[pyqt6] extra wasn’t installed → pip install edof[pyqt6]Scripts/ directory isn’t in PATHCheck where the script lives:
# Windows
where edof-editor
# Linux/macOS
which edof-editor
If not found:
import sys, os
print(os.path.dirname(sys.executable)) # Python install directory
# Then find: <that>/Scripts/edof-editor.exe (Windows) or <that>/bin/edof-editor (Linux/macOS)
Add the appropriate Scripts/bin directory to PATH (Environment Variables on Windows; ~/.bashrc on Linux).
ImportError: cryptography when using encryptionpip install edof[crypto]
# or directly:
pip install cryptography>=42
ImportError: pymupdf when calling import_pdfpip install edof[pdf]
EdofPasswordRequired when loadingThe file is encrypted and needs a password:
doc = edof.load("secret.edof", password="...")
Or with the recovery key:
doc = edof.load("secret.edof", recovery_key="ABCD-EFGH-...")
If you don’t have either, the file is unrecoverable.
EdofWrongPasswordCheck:
For recovery keys, dashes and case are ignored. 7K3F-9XQM-2N8P and 7K3F9XQM2N8P work the same.
EdofVersionError when loadingThe file was saved by a newer edof version, or the file is corrupt.
Check the version:
from edof.format.serializer import EdofSerializer
manifest = EdofSerializer.peek("file.edof")
print(manifest.get("edof_version"))
If the version is 4.0.x and you have 4.0.0, upgrade:
pip install --upgrade edof
If the file shows no version (raw extraction shows no manifest.json), it might be:
unzip -t file.edof to verify integritydata.json) — should auto-migrate; if not, file a bug.edof file — check with file file.edof (Linux/macOS) or look at the first 4 bytes; ZIP starts with PK\x03\x04EdofValidationErrorYou called doc.validate() and it found issues. The exception has a list of problem strings:
issues = doc.validate()
for i in issues:
print(f" - {i}")
Common issues and fixes:
| Issue | Fix |
|---|---|
| “Required variable X has no value” | Call doc.set_variable("X", value) before exporting |
| “Object Y references resource Z which doesn’t exist” | The resource was deleted but objects still reference it. Either re-add the resource or update the object’s resource_id |
| “Duplicate object ID” | Two objects have the same UUID. Re-create one of them with obj.id = new_uuid() |
| “Object positioned entirely off-page” | Check obj.transform.x/y — likely negative or beyond page size |
validate() doesn’t fix issues — it just reports them. Always check after building documents programmatically.
The vector PDF writer uses WinAnsiEncoding which covers Czech, Polish, German, etc. fine. If diacritics are missing:
If a specific Unicode character doesn’t render, the font you specified doesn’t have that glyph. Try a different font (DejaVu Sans is the safest default) or use raster output mode.
The vector PDF writer only supports the Standard 14 PDF fonts (Helvetica, Times, Courier with bold/italic variants). Other fonts are mapped to the closest match.
For custom fonts in PDF, use raster mode:
doc.export_pdf("output.pdf", vector=False)
This goes through Pillow which can render any installed TTF.
If ib.variable = "logo" and you change doc.set_variable("logo", new_path), but the rendered output doesn’t show the new image:
doc.resources) or a file path (string pointing to disk). If you pass a resource_id that doesn’t exist, the variable is silently ignored.# Verify the variable was set
print(doc.variables.get("logo"))
# Verify the file exists
import os
print(os.path.exists(doc.variables.get("logo")))
page.repeat_objects() calculates total height based on the bounding box of the template objects + gap. If your template has an unusual layout (nested groups, overlapping elements), the height may be misjudged.
Workaround: explicitly set a height on the template:
# Template with explicit height
group_template = some_group_with_known_size
group_template.transform.height = 25.0 # mm
# Now repeat_objects knows exactly how much space each repetition needs
Run from a terminal to see error output:
edof-editor
If you see PyQt errors:
pip show PyQt6pip install --upgrade PyQt6qt6-base-dev system packagesIf you see edof errors:
edof-editor (no arguments)python -c "import edof; print(edof.__version__)"Often a graphics driver issue:
QT_QPA_PLATFORM=xcb edof-editorQT_QPA_PLATFORM=wayland or xcb helpsCoordinates are in mm, with (0, 0) at top-left. Y increases downward. If your object appears upside down or off-page:
mm = pixels * 25.4 / dpiy_top = page.height - y_bottom - heightSmaller than expected — make sure you’re exporting in the right mode:
doc.export_pdf("out.pdf") # vector — typically 5-10 KB per page
doc.export_pdf("out.pdf", vector=False) # raster — typically 80-200 KB per page
Larger than expected — usually photographic images. Check your resources:
total = 0
for rid, info in doc.resources.list().items():
print(f" {rid}: {info['filename']} {info['size']:,} bytes")
total += info['size']
print(f"Total resources: {total:,} bytes")
If image sizes are large, downscale them before adding (Pillow can do this in 5 lines).
Check:
print(doc.variables.values()) # what's currently set?
print(doc.variables.names()) # what's defined?
If the variable is defined but not set, the placeholder remains as {name} and a warning is added to doc.errors.
If you typed {recipient} but defined recipient_name, the names don’t match — placeholders are exact.
EdofMissingFontWarning in doc.errorsA font was requested but not found. Check:
from edof.engine.text_engine import discovered_fonts
print(discovered_fonts())
Either install the font or pick a different one.
The render still happens with a fallback font (DejaVu Sans) — it’s just a warning.
Rough times on modern hardware:
If you’re seeing 5+ seconds per page:
See cookbook/batch-pdf.md for performance tips. Key ones:
Possible causes:
Mitigation:
# Periodically restart the worker process
if i % 1000 == 0:
# ... persist state, restart from clean slate
In practice, memory stays stable for batches of thousands of documents.
Try standard ZIP recovery tools:
zip -F broken.edof --out fixed.edof
If the ZIP is structurally OK but manifest.json is corrupt:
unzip broken.edof manifest.jsonzip fixed.edof manifest.json document.json resources/*For encrypted files, manifest corruption usually means the file is unrecoverable. The protection.slots data is critical — without it, no password can unlock the document.
Could be:
doc.errors should have warnings.If you’ve ruled out the issues above:
pip install --upgrade edofpython -c "import edof; print(edof.__version__)"python --versionhttps://github.com/DavidSchobl/edof/issuesIf the bug involves a specific file, include it (or a redacted/synthetic version that reproduces the issue).
docs/ directoryexamples/ directory in the repo (if present)https://github.com/DavidSchobl/edof/issues