pdf-sign-kit 0.5.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.
- pdf_sign_kit-0.5.0/.gitignore +24 -0
- pdf_sign_kit-0.5.0/LICENSE +21 -0
- pdf_sign_kit-0.5.0/PKG-INFO +366 -0
- pdf_sign_kit-0.5.0/README.md +334 -0
- pdf_sign_kit-0.5.0/pyproject.toml +88 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/__init__.py +190 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/anchor.py +213 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/background.py +55 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/compose.py +142 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/exceptions.py +27 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/fonts.py +195 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/geometry.py +130 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/hashing.py +38 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/identity.py +45 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/keywords.py +70 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/logtable.py +199 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/placement.py +401 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/preview.py +89 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/scheme.py +398 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/seam.py +186 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/stamps.py +129 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/template.py +166 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/text.py +260 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/watermark.py +186 -0
- pdf_sign_kit-0.5.0/src/pdf_sign_kit/workflow.py +474 -0
- pdf_sign_kit-0.5.0/tests/__init__.py +0 -0
- pdf_sign_kit-0.5.0/tests/conftest.py +63 -0
- pdf_sign_kit-0.5.0/tests/test_background.py +39 -0
- pdf_sign_kit-0.5.0/tests/test_geometry.py +50 -0
- pdf_sign_kit-0.5.0/tests/test_hashing.py +24 -0
- pdf_sign_kit-0.5.0/tests/test_identity.py +21 -0
- pdf_sign_kit-0.5.0/tests/test_logtable_keywords.py +90 -0
- pdf_sign_kit-0.5.0/tests/test_new_features.py +343 -0
- pdf_sign_kit-0.5.0/tests/test_placement.py +121 -0
- pdf_sign_kit-0.5.0/tests/test_preview.py +29 -0
- pdf_sign_kit-0.5.0/tests/test_public_api.py +61 -0
- pdf_sign_kit-0.5.0/tests/test_scheme.py +385 -0
- pdf_sign_kit-0.5.0/tests/test_stamps.py +35 -0
- pdf_sign_kit-0.5.0/tests/test_text.py +35 -0
- pdf_sign_kit-0.5.0/tests/test_workflow.py +132 -0
- pdf_sign_kit-0.5.0/tests/test_workflow_edge_cases.py +81 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Byte-compiled / optimized files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
*.egg
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
|
|
16
|
+
# Test / coverage
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# OS / editor
|
|
22
|
+
.DS_Store
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 兰衣
|
|
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.
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pdf-sign-kit
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Framework-agnostic Python toolkit for stamping signatures and text onto PDF: automatic table-cell detection, text-anchor placement, seam stamps, watermarks, placement templates, a paperless workflow rule engine with configurable signing levels, log tables, cross-platform CJK fonts, page preview and integrity hashing.
|
|
5
|
+
Author-email: 兰衣 <iehjli@163.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: chinese,cjk,paperless,pdf,pymupdf,sign,signature,stamp
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Office/Business
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Requires-Dist: opencv-python-headless>=4.8
|
|
24
|
+
Requires-Dist: pillow>=10
|
|
25
|
+
Requires-Dist: pymupdf>=1.23
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine>=5; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# pdf-sign-kit
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/pdf-sign-kit/)
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
[English](#english) | [中文文档](#中文文档)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<a id="english"></a>
|
|
44
|
+
## English
|
|
45
|
+
|
|
46
|
+
A framework-agnostic Python toolkit for stamping signatures and text onto PDF
|
|
47
|
+
files. It is extracted from the core of a production paperless-approval system
|
|
48
|
+
and works with **plain files** — no Django, no database, no network required.
|
|
49
|
+
|
|
50
|
+
### Features
|
|
51
|
+
|
|
52
|
+
- **Image stamps** — place any PNG (signature, seal) at a click point.
|
|
53
|
+
- **Automatic table-cell detection** — finds the enclosing cell from a single
|
|
54
|
+
grayscale raster and scales the stamp proportionally; rotated pages are
|
|
55
|
+
handled transparently.
|
|
56
|
+
- **Handwriting mode** — loose-fit scaling for handwritten signatures.
|
|
57
|
+
- **Raster text (600 DPI)** — mixed Chinese/Latin text, e.g.
|
|
58
|
+
`编号 BZ-001`, rendered with auto-detected CJK and Latin fonts; supports
|
|
59
|
+
automatic wrapping.
|
|
60
|
+
- **Built-in stamps** — "同意" (agree) and a check mark.
|
|
61
|
+
- **Background removal** — turn near-white backgrounds into transparent PNGs.
|
|
62
|
+
- **Page preview** — render any page to a PIL image / PNG bytes.
|
|
63
|
+
- **Integrity hashing** — SHA-256 / SHA-512 / MD5 digest and verification.
|
|
64
|
+
- **Workflow rule engine** — pure functions deciding who may sign now and
|
|
65
|
+
whether every participant has finished; supports personal, personal
|
|
66
|
+
countersign and department countersign levels.
|
|
67
|
+
- **Configurable signing schemes** — define any number of levels, name each
|
|
68
|
+
one, mark a level as personal / countersign / department countersign; schemes
|
|
69
|
+
round-trip through JSON, with a built-in 8-level default.
|
|
70
|
+
- **Signature log tables** — render the sign/rejection log as a PNG or a
|
|
71
|
+
`data:` URL, with Chinese keyword extraction from rejection reasons.
|
|
72
|
+
- **Text-anchor placement** — locate a stamp position by searching visible
|
|
73
|
+
text (`"签字:"`) instead of table borders; supports "after / below / over"
|
|
74
|
+
the matched text, for borderless contracts and blank templates.
|
|
75
|
+
- **Seam stamps (骑缝章)** — slice one seal into N strips placed on the edges
|
|
76
|
+
of N consecutive pages; the strips reassemble when pages are fanned.
|
|
77
|
+
- **Diagonal watermarks** — rotated "作废" / "草稿" / "保密" captions,
|
|
78
|
+
single centered mark or tiled grid, with RGBA opacity control.
|
|
79
|
+
- **Placement templates** — save reusable signature positions as JSON and
|
|
80
|
+
instantiate them with a `ref -> image` mapping for batch signing of files
|
|
81
|
+
sharing one layout.
|
|
82
|
+
- **Bitmap composition** — horizontal/vertical concatenation of images with
|
|
83
|
+
alignment, gaps and proportional scaling.
|
|
84
|
+
|
|
85
|
+
### Installation
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install pdf-sign-kit
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Quickstart
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import pdf_sign_kit as psk
|
|
95
|
+
|
|
96
|
+
# Stamp a signature image at (x, y), editing the file in place.
|
|
97
|
+
psk.stamp("form.pdf", "signature.png", x=200, y=627)
|
|
98
|
+
|
|
99
|
+
# Built-in stamps
|
|
100
|
+
psk.stamp_agree("form.pdf", x=200, y=627)
|
|
101
|
+
psk.stamp_checkmark("form.pdf", x=200, y=627)
|
|
102
|
+
|
|
103
|
+
# Mixed Chinese/Latin machine text
|
|
104
|
+
psk.stamp_text("form.pdf", "编号 BZ-001", x=200, y=627)
|
|
105
|
+
|
|
106
|
+
# Make a handwriting scan transparent
|
|
107
|
+
img = psk.remove_background("scan.png", threshold=235)
|
|
108
|
+
img.save("stamp.png")
|
|
109
|
+
|
|
110
|
+
# Render a preview / compute integrity hash
|
|
111
|
+
preview = psk.render_page("form.pdf", page=0, zoom=3)
|
|
112
|
+
digest = psk.hash_file("form.pdf")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Define a signing scheme
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from pdf_sign_kit import FlowScheme, Initiator, build_metadata
|
|
119
|
+
from pdf_sign_kit import GROUP, DEPARTMENT
|
|
120
|
+
|
|
121
|
+
# Any number of levels, custom names; level 2 is personal countersign and
|
|
122
|
+
# level 3 is department countersign (each department needs one signer).
|
|
123
|
+
scheme = FlowScheme.create(
|
|
124
|
+
3,
|
|
125
|
+
names={1: "起草", 2: "会签", 3: "部门会签"},
|
|
126
|
+
modes={2: GROUP, 3: DEPARTMENT},
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
metadata = build_metadata(
|
|
130
|
+
scheme,
|
|
131
|
+
{
|
|
132
|
+
1: [{"id": "A", "name": "甲"}],
|
|
133
|
+
2: [{"id": "B"}, {"id": "C"}], # any one signs -> done
|
|
134
|
+
3: [
|
|
135
|
+
{"id": "D", "group": "D1", "group_name": "技术部"},
|
|
136
|
+
{"id": "E", "group": "D2", "group_name": "质保部"},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
initiator=Initiator(id="I", name="initiator"),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Ask the rule engine who may sign / whether the document is complete.
|
|
143
|
+
psk.workflow.can_sign(metadata, "A")
|
|
144
|
+
psk.workflow.all_completed(metadata)
|
|
145
|
+
|
|
146
|
+
# Schemes serialize to plain JSON for storage and reloading.
|
|
147
|
+
restored = FlowScheme.from_dict(scheme.to_dict())
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Text anchor, seam stamp, watermark
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# Find visible text and stamp next to it (no table borders needed)
|
|
154
|
+
anchor = psk.find_text("contract.pdf", "签字:")
|
|
155
|
+
psk.stamp_at_anchor("contract.pdf", "signature.png", anchor)
|
|
156
|
+
|
|
157
|
+
# Seam stamp across all pages (right edges)
|
|
158
|
+
psk.stamp_seam("contract.pdf", "seal.png")
|
|
159
|
+
|
|
160
|
+
# Diagonal watermark on every page; RGBA color controls opacity
|
|
161
|
+
psk.stamp_watermark(
|
|
162
|
+
"contract.pdf", "作废",
|
|
163
|
+
pages=[0, 1],
|
|
164
|
+
color=(220, 38, 38, 90),
|
|
165
|
+
tile=True,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Compose a name + date strip before stamping
|
|
169
|
+
combined = psk.hconcat([name_img, date_img], gap=8)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Save and reuse placement templates
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from pdf_sign_kit import PlacementTemplate
|
|
176
|
+
|
|
177
|
+
# Capture positions from stamp_batch operations and store as JSON
|
|
178
|
+
template = PlacementTemplate.capture(operations, name="Form-A")
|
|
179
|
+
psk.save_template("form_a.json", template)
|
|
180
|
+
|
|
181
|
+
# Later: reuse the same positions on a new file of the same layout
|
|
182
|
+
template = psk.load_template("form_a.json")
|
|
183
|
+
operations = template.instantiate({"signature": "sig.png", "date": "date.png"})
|
|
184
|
+
psk.stamp_batch("another_file.pdf", operations)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Coordinate system
|
|
188
|
+
|
|
189
|
+
- `x` / `y` are **PDF points** (1 pt = 1/72 inch) measured from the page's
|
|
190
|
+
**bottom-left** corner in its display orientation.
|
|
191
|
+
- `page` indices are **zero-based**.
|
|
192
|
+
- Page rotation is handled internally.
|
|
193
|
+
|
|
194
|
+
### Notes & limitations
|
|
195
|
+
|
|
196
|
+
- These are **visual ("electronic") signatures** — raster images placed on the
|
|
197
|
+
page — not PKI/certificate-based ("digital") signatures with legal
|
|
198
|
+
non-repudiation guarantees.
|
|
199
|
+
- Raster text is flattened (not selectable), which matches the print-first
|
|
200
|
+
quality requirement.
|
|
201
|
+
- No fonts are bundled: CJK/Latin fonts are auto-detected on the system; you
|
|
202
|
+
may always pass an explicit `font_path`. On Debian/Ubuntu:
|
|
203
|
+
`apt-get install fonts-noto-cjk`.
|
|
204
|
+
|
|
205
|
+
### License
|
|
206
|
+
|
|
207
|
+
[MIT](LICENSE) © 兰衣
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
<a id="中文文档"></a>
|
|
212
|
+
## 中文文档
|
|
213
|
+
|
|
214
|
+
一个与 Web 框架无关的 Python 工具包,用于在 PDF 文件上加盖签名图章与机打
|
|
215
|
+
文字。代码提炼自一套生产环境无纸化审批系统的核心内核,**只操作本地文件**
|
|
216
|
+
——不依赖 Django、数据库或网络。
|
|
217
|
+
|
|
218
|
+
### 功能特性
|
|
219
|
+
|
|
220
|
+
- **图片盖章**:在指定坐标放置任意 PNG(签名、印章)。
|
|
221
|
+
- **表格单元格自动检测**:通过一次灰度渲染识别点击位置所在的表格格子,
|
|
222
|
+
并按格子比例自动缩放;自动处理页面旋转。
|
|
223
|
+
- **手写模式**:为手写签名提供宽松缩放策略。
|
|
224
|
+
- **机打文字(600 DPI)**:支持中英混排(如 `编号 BZ-001`),自动匹配中/英
|
|
225
|
+
字体,支持自动换行。
|
|
226
|
+
- **内置图章**:"同意" 章与打勾(check mark)。
|
|
227
|
+
- **去背景**:将近白背景转为透明 PNG。
|
|
228
|
+
- **页面预览**:把任意页面渲染为 PIL 图像 / PNG 字节。
|
|
229
|
+
- **完整性哈希**:支持 SHA-256 / SHA-512 / MD5 计算与校验。
|
|
230
|
+
- **签署流程规则引擎**:以纯函数判断当前谁可以签、是否全部签署完成,支持
|
|
231
|
+
个人签署、个人会签与部门会签。
|
|
232
|
+
- **签署等级方案可配置**:可自定义任意数量的等级、为每个等级命名,标记某级
|
|
233
|
+
为个人签署/会签/部门会签;方案支持 JSON 序列化,并内置一套 8 级默认方案。
|
|
234
|
+
- **签字日志表格**:将签字/驳回日志渲染为 PNG 或 `data:` URL,并支持从驳回
|
|
235
|
+
原因中提取中文关键词。
|
|
236
|
+
- **文本锚点定位**:通过搜索可见文字(如「签字:」)确定盖章位置,而非依赖
|
|
237
|
+
表格线;支持在匹配文字「其后/其下/覆盖」定位,适用于无表格线的合同与空白
|
|
238
|
+
模板。
|
|
239
|
+
- **骑缝章**:将一枚印章切成 N 条,分别盖在连续 N 页的边缘,展开拼合后还原
|
|
240
|
+
完整印章。
|
|
241
|
+
- **斜向水印**:「作废」「草稿」「保密」等旋转文字,支持单个居中章或整页平铺,
|
|
242
|
+
RGBA 颜色直接控制透明度。
|
|
243
|
+
- **签署位置模板**:把可复用的签署位置保存为 JSON,通过「引用名 → 图片」映射
|
|
244
|
+
实例化,用于同版式文件的批量签署。
|
|
245
|
+
- **位图拼接**:多图水平/垂直拼接,支持对齐、间距与等比缩放。
|
|
246
|
+
|
|
247
|
+
### 安装
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
pip install pdf-sign-kit
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 快速上手
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
import pdf_sign_kit as psk
|
|
257
|
+
|
|
258
|
+
# 在 (x, y) 处加盖签名图片,原地修改文件
|
|
259
|
+
psk.stamp("表单.pdf", "签名.png", x=200, y=627)
|
|
260
|
+
|
|
261
|
+
# 内置图章
|
|
262
|
+
psk.stamp_agree("表单.pdf", x=200, y=627)
|
|
263
|
+
psk.stamp_checkmark("表单.pdf", x=200, y=627)
|
|
264
|
+
|
|
265
|
+
# 中英混排机打文字
|
|
266
|
+
psk.stamp_text("表单.pdf", "编号 BZ-001", x=200, y=627)
|
|
267
|
+
|
|
268
|
+
# 将手写扫描件去背景
|
|
269
|
+
img = psk.remove_background("扫描件.png", threshold=235)
|
|
270
|
+
img.save("图章.png")
|
|
271
|
+
|
|
272
|
+
# 渲染预览图 / 计算完整性哈希
|
|
273
|
+
preview = psk.render_page("表单.pdf", page=0, zoom=3)
|
|
274
|
+
digest = psk.hash_file("表单.pdf")
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 定义签署等级方案
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from pdf_sign_kit import FlowScheme, Initiator, build_metadata
|
|
281
|
+
from pdf_sign_kit import GROUP, DEPARTMENT
|
|
282
|
+
|
|
283
|
+
# 任意等级数量、自定义名称;第2级为个人会签,第3级为部门会签(每个部门
|
|
284
|
+
# 任一人签署即完成该部门)。
|
|
285
|
+
scheme = FlowScheme.create(
|
|
286
|
+
3,
|
|
287
|
+
names={1: "起草", 2: "会签", 3: "部门会签"},
|
|
288
|
+
modes={2: GROUP, 3: DEPARTMENT},
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
metadata = build_metadata(
|
|
292
|
+
scheme,
|
|
293
|
+
{
|
|
294
|
+
1: [{"id": "A", "name": "甲"}],
|
|
295
|
+
2: [{"id": "B"}, {"id": "C"}], # 任一人签即完成
|
|
296
|
+
3: [
|
|
297
|
+
{"id": "D", "group": "D1", "group_name": "技术部"},
|
|
298
|
+
{"id": "E", "group": "D2", "group_name": "质保部"},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
initiator=Initiator(id="I", name="发起人"),
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# 由规则引擎判断谁可签 / 文件是否全部完成
|
|
305
|
+
psk.workflow.can_sign(metadata, "A")
|
|
306
|
+
psk.workflow.all_completed(metadata)
|
|
307
|
+
|
|
308
|
+
# 方案可序列化为普通 JSON 存储后再恢复
|
|
309
|
+
restored = FlowScheme.from_dict(scheme.to_dict())
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 文本锚点、骑缝章、水印
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
# 搜索可见文字并在其后盖章(无需表格线)
|
|
316
|
+
anchor = psk.find_text("合同.pdf", "签字:")
|
|
317
|
+
psk.stamp_at_anchor("合同.pdf", "签名.png", anchor)
|
|
318
|
+
|
|
319
|
+
# 对所有页加盖骑缝章(右边缘)
|
|
320
|
+
psk.stamp_seam("合同.pdf", "印章.png")
|
|
321
|
+
|
|
322
|
+
# 在指定页加盖斜向水印;RGBA 颜色控制透明度
|
|
323
|
+
psk.stamp_watermark(
|
|
324
|
+
"合同.pdf", "作废",
|
|
325
|
+
pages=[0, 1],
|
|
326
|
+
color=(220, 38, 38, 90),
|
|
327
|
+
tile=True,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# 盖章前先拼接姓名 + 日期
|
|
331
|
+
combined = psk.hconcat([name_img, date_img], gap=8)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 保存与复用签署位置模板
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from pdf_sign_kit import PlacementTemplate
|
|
338
|
+
|
|
339
|
+
# 从 stamp_batch 操作中提取位置并存为 JSON
|
|
340
|
+
template = PlacementTemplate.capture(operations, name="表单A")
|
|
341
|
+
psk.save_template("表单_a.json", template)
|
|
342
|
+
|
|
343
|
+
# 之后在同版式的新文件上复用相同位置
|
|
344
|
+
template = psk.load_template("表单_a.json")
|
|
345
|
+
operations = template.instantiate({"signature": "签名.png", "date": "日期.png"})
|
|
346
|
+
psk.stamp_batch("另一个文件.pdf", operations)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### 坐标系说明
|
|
350
|
+
|
|
351
|
+
- `x` / `y` 单位为 **PDF 点**(1 pt = 1/72 英寸),以页面显示方向的
|
|
352
|
+
**左下角**为原点。
|
|
353
|
+
- `page` 页码从 **0** 开始。
|
|
354
|
+
- 页面旋转由库内部处理。
|
|
355
|
+
|
|
356
|
+
### 注意与限制
|
|
357
|
+
|
|
358
|
+
- 本包提供的是**可视化("电子")签名**——即放置到页面上的图片,而非基于
|
|
359
|
+
PKI/证书、具备法律防抵赖能力的"数字签名"。
|
|
360
|
+
- 机打文字以栅格方式写入(不可选中复制),以满足高打印质量要求。
|
|
361
|
+
- 包不携带字体:会自动探测系统中的中/英字体,也可显式传入 `font_path`;
|
|
362
|
+
Debian/Ubuntu 可执行 `apt-get install fonts-noto-cjk`。
|
|
363
|
+
|
|
364
|
+
### 许可证
|
|
365
|
+
|
|
366
|
+
[MIT](LICENSE) © 兰衣
|