edof 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. edof-3.0.0/CHANGELOG.md +92 -0
  2. edof-3.0.0/LICENSE +21 -0
  3. edof-3.0.0/MANIFEST.in +5 -0
  4. edof-3.0.0/PKG-INFO +473 -0
  5. edof-3.0.0/README.md +405 -0
  6. edof-3.0.0/edof/__init__.py +91 -0
  7. edof-3.0.0/edof/api/__init__.py +0 -0
  8. edof-3.0.0/edof/api/commands.py +326 -0
  9. edof-3.0.0/edof/engine/__init__.py +1 -0
  10. edof-3.0.0/edof/engine/color.py +90 -0
  11. edof-3.0.0/edof/engine/renderer.py +259 -0
  12. edof-3.0.0/edof/engine/text_engine.py +251 -0
  13. edof-3.0.0/edof/engine/transform.py +241 -0
  14. edof-3.0.0/edof/exceptions.py +78 -0
  15. edof-3.0.0/edof/export/__init__.py +1 -0
  16. edof-3.0.0/edof/export/bitmap.py +92 -0
  17. edof-3.0.0/edof/export/pdf.py +62 -0
  18. edof-3.0.0/edof/export/printer.py +119 -0
  19. edof-3.0.0/edof/format/__init__.py +1 -0
  20. edof-3.0.0/edof/format/document.py +509 -0
  21. edof-3.0.0/edof/format/objects.py +358 -0
  22. edof-3.0.0/edof/format/serializer.py +149 -0
  23. edof-3.0.0/edof/format/styles.py +176 -0
  24. edof-3.0.0/edof/format/variables.py +155 -0
  25. edof-3.0.0/edof/gui/__init__.py +0 -0
  26. edof-3.0.0/edof/gui/pyqt6_widget.py +254 -0
  27. edof-3.0.0/edof/gui/tkinter_canvas.py +541 -0
  28. edof-3.0.0/edof/py.typed +0 -0
  29. edof-3.0.0/edof/utils/__init__.py +1 -0
  30. edof-3.0.0/edof/utils/compat.py +82 -0
  31. edof-3.0.0/edof/utils/qr.py +69 -0
  32. edof-3.0.0/edof/version.py +38 -0
  33. edof-3.0.0/edof.egg-info/PKG-INFO +473 -0
  34. edof-3.0.0/edof.egg-info/SOURCES.txt +39 -0
  35. edof-3.0.0/edof.egg-info/dependency_links.txt +1 -0
  36. edof-3.0.0/edof.egg-info/requires.txt +24 -0
  37. edof-3.0.0/edof.egg-info/top_level.txt +1 -0
  38. edof-3.0.0/pyproject.toml +91 -0
  39. edof-3.0.0/setup.cfg +4 -0
  40. edof-3.0.0/tests/test_document.py +110 -0
  41. edof-3.0.0/tests/test_transform.py +111 -0
@@ -0,0 +1,92 @@
1
+ # Changelog
2
+
3
+ All notable changes to **edof** are documented here.
4
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning: [SemVer](https://semver.org/)
5
+
6
+ ---
7
+
8
+ ## [3.0.0] – 2025-01-01
9
+
10
+ Initial public release.
11
+
12
+ ### Library
13
+
14
+ #### Document model
15
+ - `Document`, `Page`, `ResourceStore` – root structure
16
+ - Object types: `TextBox`, `ImageBox`, `Shape` (rect/ellipse/polygon/arrow), `Line` (two absolute points), `QRCode`, `Group`
17
+ - Common object properties: `id`, `name`, `variable`, `layer`, `locked`, `visible`, `editable`, `tags`, `opacity`, `shadow`
18
+ - `TextStyle` – font, size, bold/italic/underline/strikethrough, color, alignment (H+V), line height, word wrap, overflow
19
+ - `auto_shrink` – shrinks font to fit the box, never enlarges (font_size = maximum)
20
+ - `auto_fill` – fills the box by finding the largest fitting font size (grows + shrinks)
21
+ - `StrokeStyle`, `FillStyle`, `ShadowStyle` – RGBA color support throughout
22
+ - `Transform` – position/size in mm, clockwise rotation in °, flip H/V; full chain API
23
+
24
+ #### Variable / template system
25
+ - `VariableStore` – named placeholders with type validation (`text`, `number`, `date`, `image`, `qr`, `url`, `bool`)
26
+ - `doc.fill_variables({"name": "Jan"})` – batch fill for template rendering
27
+ - `ImageBox` variable – value can be a file path or HTTP URL loaded at render time
28
+ - Fallback: if variable is unset/empty, `obj.text` is displayed (non-destructive in editor)
29
+
30
+ #### Rendering
31
+ - Pillow RGBA compositor; color spaces: RGB, RGBA, L, 1, CMYK; bit depths: 8 and 16
32
+ - Configurable DPI per page
33
+ - Correct `pt → px` DPI conversion throughout (font sizing, auto-shrink/fill)
34
+ - Rotation applies to the entire object surface (fill + border + text as one unit)
35
+ - QR colorization: generates B&W first, colorizes pixel-by-pixel → all fg/bg colors work
36
+
37
+ #### File format (`.edof`)
38
+ - ZIP archive: `manifest.json` + `document.json` + `resources/<uuid>` (embedded fonts, images)
39
+ - `EdofSerializer` – `save`, `load`, `to_bytes`, `from_bytes`, `peek`
40
+ - Forward compat: newer file → `EdofNewerVersionWarning` (non-fatal, execution continues)
41
+ - Backward compat: older files migrated automatically via `edof.utils.compat`
42
+
43
+ #### Export & print
44
+ - Bitmap: PNG, JPEG, TIFF, BMP; all pages with `{page}` pattern; in-memory bytes
45
+ - PDF via reportlab (`pip install edof[pdf]`)
46
+ - Print: `QPrintPreviewDialog` (PyQt6), `os.startfile` (Windows), `lpr`/`lp` (macOS/Linux)
47
+
48
+ #### Text engine
49
+ - System font discovery on Windows / macOS / Linux via `font.getname()` metadata
50
+ - Bold/italic variant resolution, font cache, `list_system_fonts()`
51
+ - Text wrap, multi-line, vertical alignment, underline, strikethrough
52
+
53
+ #### QR codes
54
+ - `edof[qr]` optional dep; error correction L/M/Q/H; RGBA colors
55
+ - Standalone helpers: `edof.utils.qr.generate_qr_image()`, `generate_qr_bytes()`
56
+
57
+ #### Command API & undo/redo
58
+ - `edof.api.commands.execute(doc, {"cmd": "...", ...})` – string-based dispatch
59
+ - `CommandHistory` – snapshot-based undo/redo stack
60
+
61
+ #### GUI
62
+ - `EdofTkCanvas` – Tkinter canvas widget
63
+ - `EdofQtWidget` – PyQt6 QGraphicsView widget
64
+
65
+ ### Applications
66
+
67
+ #### EDOF Editor (`edof_editor.py`) — requires PyQt6
68
+ - Async rendering (background thread, UI never blocks)
69
+ - Resize handles (8 directions) + rotation handle; rotated object resize keeps anchor fixed
70
+ - Shift = snap rotation to 15°, Alt = free rotation
71
+ - Inline text editing (double-click): WYSIWYG font size matches canvas zoom and screen DPI
72
+ - Double-click QR → inline data/URL editor; double-click Image → file picker
73
+ - Ghost outlines for hidden objects (still selectable)
74
+ - Type-aware property panel: TextBox / ImageBox / Shape / Line / QRCode each has own controls
75
+ - Custom color dialog: hex `#RRGGBBAA`, RGBA sliders, live swatch
76
+ - Object list panel, page list panel, layer ordering (front/back/up/down)
77
+ - Print preview via `QPrintPreviewDialog`, renders at ≤150 dpi using raw PIL bytes (bypasses Qt 256 MB limit)
78
+ - Internationalisation via `editor_lang/XX.json`
79
+
80
+ #### EDOF CLI (`edof_cli.py`)
81
+ - `info`, `objects`, `validate`, `export` sub-commands
82
+ - `--set key=value`, `--json-vars '{...}'`, `--all-pages`, `--dpi`, `--format`, `--color-space`
83
+
84
+ ---
85
+
86
+ ## Unreleased
87
+
88
+ Nothing yet.
89
+
90
+ ---
91
+
92
+ > Versions 1.x and 2.x were internal development iterations and were never publicly released.
edof-3.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 EDOF Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
edof-3.0.0/MANIFEST.in ADDED
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include edof/py.typed
5
+ recursive-include edof *.py
edof-3.0.0/PKG-INFO ADDED
@@ -0,0 +1,473 @@
1
+ Metadata-Version: 2.4
2
+ Name: edof
3
+ Version: 3.0.0
4
+ Summary: Easy Document Format – programmatic document creation, editing and export
5
+ License: MIT License
6
+
7
+ Copyright (c) 2025 EDOF Contributors
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/DavidSchobl/edof
28
+ Project-URL: Documentation, https://github.com/DavidSchobl/edof#readme
29
+ Project-URL: Repository, https://github.com/DavidSchobl/edof
30
+ Project-URL: Bug Tracker, https://github.com/DavidSchobl/edof/issues
31
+ Project-URL: Changelog, https://github.com/DavidSchobl/edof/blob/main/CHANGELOG.md
32
+ Keywords: document,format,pdf,template,edof,print
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.9
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Programming Language :: Python :: 3.13
42
+ Classifier: Topic :: Multimedia :: Graphics
43
+ Classifier: Topic :: Office/Business
44
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
45
+ Requires-Python: >=3.9
46
+ Description-Content-Type: text/markdown
47
+ License-File: LICENSE
48
+ Requires-Dist: Pillow>=9.1
49
+ Provides-Extra: pdf
50
+ Requires-Dist: reportlab>=4.0; extra == "pdf"
51
+ Provides-Extra: qr
52
+ Requires-Dist: qrcode[pil]>=7.4; extra == "qr"
53
+ Provides-Extra: pyqt6
54
+ Requires-Dist: PyQt6>=6.6; extra == "pyqt6"
55
+ Provides-Extra: all
56
+ Requires-Dist: reportlab>=4.0; extra == "all"
57
+ Requires-Dist: qrcode[pil]>=7.4; extra == "all"
58
+ Requires-Dist: PyQt6>=6.6; extra == "all"
59
+ Provides-Extra: dev
60
+ Requires-Dist: pytest>=7.0; extra == "dev"
61
+ Requires-Dist: pytest-cov; extra == "dev"
62
+ Requires-Dist: black; extra == "dev"
63
+ Requires-Dist: ruff; extra == "dev"
64
+ Requires-Dist: mypy; extra == "dev"
65
+ Requires-Dist: build; extra == "dev"
66
+ Requires-Dist: twine; extra == "dev"
67
+ Dynamic: license-file
68
+
69
+ # edof – Easy Document Format 3.0
70
+
71
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
72
+ [![Python](https://img.shields.io/badge/python-3.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue)](https://pypi.org/project/edof/)
73
+ [![Tests](https://github.com/DavidSchobl/edof/actions/workflows/publish.yml/badge.svg)](https://github.com/DavidSchobl/edof/actions)
74
+ [![PyPI](https://img.shields.io/badge/PyPI-coming%20soon-lightgrey)](https://pypi.org/project/edof/)
75
+
76
+ **edof** is a Python library for programmatic document creation, template filling and high-quality export.
77
+ Documents are stored as `.edof` files – a versioned ZIP archive with a JSON document tree and all embedded resources (fonts, images).
78
+
79
+ ```python
80
+ import edof
81
+
82
+ doc = edof.new(width=210, height=297, title="My Certificate")
83
+ page = doc.add_page(dpi=300)
84
+
85
+ tb = page.add_textbox(10, 20, 190, 30, "")
86
+ tb.variable = "recipient" # bound to a template variable
87
+ tb.style.font_size = 48
88
+ tb.style.auto_shrink = True # shrinks if text doesn't fit, never enlarges
89
+ tb.style.alignment = "center"
90
+
91
+ doc.define_variable("recipient", required=True)
92
+ doc.fill_variables({"recipient": "Jan Novák"})
93
+ doc.save("certificate.edof")
94
+ doc.export_bitmap("certificate.png", dpi=300)
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Installation
100
+
101
+ ```bash
102
+ # Core library (Pillow only)
103
+ pip install edof
104
+
105
+ # With PDF export
106
+ pip install edof[pdf]
107
+
108
+ # With QR code generation
109
+ pip install edof[qr]
110
+
111
+ # With PyQt6 editor / widget
112
+ pip install edof[pyqt6]
113
+
114
+ # Everything
115
+ pip install edof[all]
116
+
117
+ # Development
118
+ pip install edof[dev]
119
+ ```
120
+
121
+ **Python 3.9 – 3.13** supported.
122
+
123
+ ---
124
+
125
+ ## Quick Start
126
+
127
+ ### Create a document
128
+
129
+ ```python
130
+ import edof
131
+
132
+ doc = edof.new(width=210, height=297) # A4 in mm, 300 dpi default
133
+ page = doc.add_page()
134
+
135
+ # Text box
136
+ tb = page.add_textbox(x=10, y=10, width=190, height=30, text="Hello!")
137
+ tb.style.font_size = 36
138
+ tb.style.alignment = "center"
139
+ tb.style.bold = True
140
+
141
+ # Image
142
+ rid = doc.add_resource_from_file("logo.png")
143
+ page.add_image(rid, x=10, y=50, width=80, height=50, fit_mode="contain")
144
+
145
+ # Shape
146
+ sh = page.add_shape("rect", x=10, y=110, width=190, height=1)
147
+ sh.fill.color = (0, 0, 0, 255)
148
+
149
+ # QR code (requires pip install edof[qr])
150
+ page.add_qrcode("https://github.com/DavidSchobl/edof", x=160, y=120, size=30)
151
+
152
+ doc.save("doc.edof")
153
+ doc.export_bitmap("doc.png", dpi=300) # PNG
154
+ doc.export_pdf("doc.pdf") # PDF (requires edof[pdf])
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Templates & Batch Fill
160
+
161
+ Objects can be bound to named **variables**. At render time the variable value replaces the object's content.
162
+
163
+ ```python
164
+ doc = edof.new()
165
+ page = doc.add_page()
166
+
167
+ # Define variables with types and defaults
168
+ doc.define_variable("name", type="text", default="—", required=True)
169
+ doc.define_variable("score", type="number", default="0")
170
+ doc.define_variable("logo", type="image", default="default.png") # file path or URL
171
+
172
+ # Bind objects to variables
173
+ tb_name = page.add_textbox(10, 10, 100, 20, "")
174
+ tb_name.variable = "name"
175
+ tb_name.style.auto_shrink = True # font shrinks if name is long
176
+
177
+ img = page.add_image(None, 150, 10, 40, 40)
178
+ img.variable = "logo" # value = file path or HTTP URL
179
+
180
+ # Fill once
181
+ doc.fill_variables({"name": "Alice", "score": "98", "logo": "alice.png"})
182
+ doc.export_bitmap("alice.png")
183
+
184
+ # Batch loop
185
+ for row in [("Bob", "87", "bob.png"), ("Carol", "95", "carol.png")]:
186
+ doc.fill_variables({"name": row[0], "score": row[1], "logo": row[2]})
187
+ doc.export_bitmap(f"{row[0]}.png")
188
+ ```
189
+
190
+ ### Missing variables are non-destructive
191
+ If a variable has no value set, the object shows its `obj.text` fallback — useful for previewing a template in the editor without filling all fields first.
192
+
193
+ ---
194
+
195
+ ## Object Types
196
+
197
+ ### TextBox
198
+
199
+ ```python
200
+ tb = page.add_textbox(x, y, width, height, text="")
201
+
202
+ # Sizing modes (mutually exclusive):
203
+ tb.style.auto_shrink = True # font_size = maximum; shrinks to fit; never enlarges
204
+ tb.style.auto_fill = True # fills the box (grows and shrinks up to max_font_size)
205
+ # both False = fixed font size
206
+
207
+ tb.style.font_family = "Arial"
208
+ tb.style.font_size = 18.0 # pt
209
+ tb.style.min_font_size = 4.0 # floor for auto-shrink/fill
210
+ tb.style.max_font_size = 200.0 # ceiling for auto-fill
211
+ tb.style.bold = True
212
+ tb.style.italic = True
213
+ tb.style.underline = True
214
+ tb.style.strikethrough = True
215
+ tb.style.color = (0, 0, 0) # RGB
216
+ tb.style.alignment = "center" # left|center|right|justify
217
+ tb.style.vertical_align= "middle" # top|middle|bottom
218
+ tb.style.line_height = 1.2
219
+ tb.style.wrap = True
220
+ tb.style.overflow_hidden = True
221
+ ```
222
+
223
+ ### ImageBox
224
+
225
+ ```python
226
+ img = page.add_image(resource_id, x, y, width, height,
227
+ fit_mode="contain") # contain|cover|fill|stretch|none
228
+ # fit_mode="contain" – letterboxed, preserves aspect ratio
229
+ # fit_mode="cover" – cropped, fills the box
230
+ # fit_mode="fill" – alias for cover
231
+ # fit_mode="stretch" – distorts to fill exactly
232
+
233
+ rid = doc.add_resource_from_file("photo.jpg")
234
+ # or: rid = doc.add_resource(bytes_data, "photo.jpg", "image/jpeg")
235
+ ```
236
+
237
+ ### Shape
238
+
239
+ ```python
240
+ sh = page.add_shape("rect", x, y, width, height)
241
+ # Types: "rect" | "ellipse" | "polygon" | "arrow"
242
+
243
+ sh.fill.color = (100, 149, 237, 255) # RGBA
244
+ sh.stroke.color = (50, 80, 180, 255)
245
+ sh.stroke.width = 1.5 # pt
246
+ sh.corner_radius = 4.0 # mm (for rect)
247
+ ```
248
+
249
+ ### Line
250
+
251
+ ```python
252
+ sh = page.add_shape("line", 0, 0, 1, 1)
253
+ sh.points = [[x1_mm, y1_mm], [x2_mm, y2_mm]] # two absolute page coordinates
254
+ sh.stroke.color = (0, 0, 0, 255)
255
+ sh.stroke.width = 2.0 # pt
256
+ ```
257
+
258
+ ### QR Code
259
+
260
+ ```python
261
+ qr = page.add_qrcode(data="https://example.com", x=10, y=10,
262
+ size=40, error_correction="M")
263
+ # error_correction: "L" | "M" | "Q" | "H"
264
+ qr.fg_color = (0, 0, 0, 255) # RGBA – any color works
265
+ qr.bg_color = (255, 255, 255, 255)
266
+ qr.variable = "url" # dynamic data from variable
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Transform API
272
+
273
+ Every object supports a full transform chain:
274
+
275
+ ```python
276
+ obj.move_to(20, 30) # absolute position (mm)
277
+ obj.move(10, 5) # relative translate
278
+ obj.rotate_to(45) # absolute rotation (°, clockwise)
279
+ obj.rotate(15) # add 15° to current rotation
280
+ obj.resize_uniform(1.5) # scale both axes, keep centre
281
+ obj.resize(100, 40) # set absolute width × height (mm)
282
+ obj.resize(100, 40, anchor="center") # resize keeping centre fixed
283
+ obj.flip_h()
284
+ obj.flip_v()
285
+
286
+ # Chainable
287
+ obj.move_to(10, 10).resize(80, 30).rotate_to(15)
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Color Spaces & Bit Depth
293
+
294
+ ```python
295
+ page = doc.add_page(color_space="L", bit_depth=8) # grayscale
296
+ page = doc.add_page(color_space="1") # black & white
297
+ page = doc.add_page(color_space="RGB", bit_depth=16) # 16-bit
298
+
299
+ # Override on export
300
+ doc.export_bitmap("gray.tiff", color_space="L", format="TIFF")
301
+ doc.export_bitmap("bw.png", color_space="1")
302
+ ```
303
+
304
+ Supported values: `"RGB"`, `"RGBA"`, `"L"` (grayscale), `"1"` (B&W), `"CMYK"`.
305
+
306
+ ---
307
+
308
+ ## File Format
309
+
310
+ `.edof` is a plain ZIP archive — inspectable with any ZIP tool:
311
+
312
+ ```
313
+ document.edof
314
+ ├── manifest.json ← version header, quick metadata
315
+ ├── document.json ← full document tree (no binary blobs)
316
+ └── resources/
317
+ ├── <uuid> ← embedded image / font (raw bytes)
318
+ └── …
319
+ ```
320
+
321
+ ### Version compatibility
322
+
323
+ | File version vs library | Behaviour |
324
+ |---|---|
325
+ | Same | Full compatibility |
326
+ | File older | Loaded and migrated automatically, non-fatal notice in `doc.errors` |
327
+ | File newer | `EdofNewerVersionWarning` emitted; content loaded best-effort |
328
+ | File too old (major < 1) | `EdofVersionError` raised |
329
+
330
+ ```python
331
+ import warnings
332
+ from edof.exceptions import EdofNewerVersionWarning
333
+
334
+ with warnings.catch_warnings(record=True) as w:
335
+ warnings.simplefilter("always")
336
+ doc = edof.load("newer.edof")
337
+
338
+ print(doc.errors) # non-fatal notices from load
339
+ ```
340
+
341
+ ---
342
+
343
+ ## Command API
344
+
345
+ ```python
346
+ from edof.api.commands import execute, CommandHistory
347
+
348
+ doc = edof.new(); doc.add_page()
349
+
350
+ oid = execute(doc, {"cmd": "add_textbox", "page": 0,
351
+ "x": 10, "y": 10, "width": 80, "height": 20,
352
+ "text": "Hello API"})
353
+
354
+ execute(doc, {"cmd": "set_style", "object_id": oid, "page": 0,
355
+ "style": {"font_size": 24, "auto_shrink": True}})
356
+
357
+ execute(doc, {"cmd": "export_bitmap", "page": 0,
358
+ "path": "out.png", "dpi": 300})
359
+
360
+ # Undo / redo
361
+ history = CommandHistory(max_undo=50)
362
+ history.push(doc, "initial state")
363
+ # … make changes …
364
+ doc = history.undo(doc) # returns restored Document or None
365
+ doc = history.redo(doc)
366
+ ```
367
+
368
+ Available commands: `add_page`, `remove_page`, `add_textbox`, `add_image`, `add_shape`, `add_qrcode`, `remove_object`, `set_text`, `set_variable`, `fill_variables`, `move_object`, `resize_object`, `rotate_object`, `set_style`, `set_visibility`, `set_layer`, `export_bitmap`, `export_pdf`, `save`, `validate`.
369
+
370
+ ---
371
+
372
+ ## EDOF Editor
373
+
374
+ A full desktop document editor ships with the repository:
375
+
376
+ ```bash
377
+ pip install edof[pyqt6]
378
+ python edof_editor.py
379
+ python edof_editor.py myfile.edof
380
+ ```
381
+
382
+ **Canvas features:**
383
+ - 8 resize handles (corners + edges) with correct rotated-object resize
384
+ - Rotation handle — **Shift** = snap to 15°, **Alt** = free rotation
385
+ - Double-click `TextBox` → WYSIWYG inline text editor (font size matches canvas zoom)
386
+ - Double-click `QRCode` → inline data/URL editor
387
+ - Double-click `ImageBox` → file picker to replace the source
388
+ - Ghost outlines for hidden objects — still selectable
389
+ - Middle-mouse pan · Scroll-wheel zoom
390
+
391
+ **Property panel** — type-aware:
392
+
393
+ | Object | Properties shown |
394
+ |---|---|
395
+ | TextBox | Content (live update), font, size, color + alpha, alignment, sizing mode radio buttons |
396
+ | ImageBox | Fit mode, replace image |
397
+ | Shape | Fill color + alpha, stroke color + alpha + width, corner radius |
398
+ | Line | X1/Y1/X2/Y2 endpoints, stroke |
399
+ | QRCode | Data/URL (live update), error correction, FG/BG color + alpha |
400
+
401
+ **Other editor features:**
402
+ - Object list panel (layer order, type, name, variable, visibility, lock)
403
+ - Custom RGBA color dialog (`#RRGGBBAA` hex + sliders)
404
+ - Layer ordering: bring to front / forward / backward / send to back
405
+ - Variables dialog with live re-render on apply
406
+ - Print preview (`QPrintPreviewDialog`) with correct page rendering
407
+ - Export PNG / JPEG / TIFF / PDF
408
+ - 60-step undo/redo
409
+ - Internationalisation — add `editor_lang/XX.json` for any language (base: `en.json`)
410
+
411
+ ---
412
+
413
+ ## EDOF CLI
414
+
415
+ Fill templates and export from the command line without opening the editor:
416
+
417
+ ```bash
418
+ # Inspect a template
419
+ python edof_cli.py info template.edof
420
+ python edof_cli.py objects template.edof
421
+ python edof_cli.py validate template.edof
422
+
423
+ # Export with variables
424
+ python edof_cli.py export template.edof output.png \
425
+ --set name="Jan Novák" \
426
+ --set date="2025-01-01" \
427
+ --dpi 300
428
+
429
+ # JSON variables
430
+ python edof_cli.py export template.edof output.png \
431
+ --json-vars '{"name":"Jan","score":"98"}'
432
+
433
+ # All pages (use {page} or {n} in filename)
434
+ python edof_cli.py export template.edof page_{page}.png --all-pages
435
+
436
+ # PDF
437
+ python edof_cli.py export template.edof output.pdf
438
+ ```
439
+
440
+ | Flag | Short | Description |
441
+ |---|---|---|
442
+ | `--set KEY=VALUE` | `-s` | Set one variable (repeatable) |
443
+ | `--json-vars JSON` | `-j` | Set multiple variables as JSON |
444
+ | `--page N` | `-p` | Page index (0-based, default 0) |
445
+ | `--all-pages` | `-A` | Export all pages |
446
+ | `--format` | `-f` | `png` `jpg` `tiff` `bmp` `pdf` |
447
+ | `--dpi N` | `-d` | Resolution (default 300) |
448
+ | `--color-space` | `-c` | `RGB` `RGBA` `L` `1` `CMYK` |
449
+
450
+ ---
451
+
452
+ ## Running Tests
453
+
454
+ ```bash
455
+ pip install edof[dev]
456
+ pytest
457
+ pytest --cov=edof --cov-report=html # with coverage report
458
+ ```
459
+
460
+ ---
461
+
462
+ ## License
463
+
464
+ MIT – see [LICENSE](LICENSE).
465
+
466
+ ---
467
+
468
+ ## Links
469
+
470
+ - **PyPI:** https://pypi.org/project/edof/
471
+ - **GitHub:** https://github.com/DavidSchobl/edof
472
+ - **Issues:** https://github.com/DavidSchobl/edof/issues
473
+ - **Changelog:** [CHANGELOG.md](CHANGELOG.md)