ulpp 1.3.1__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 (94) hide show
  1. ulpp-1.3.1/LICENSE +21 -0
  2. ulpp-1.3.1/PKG-INFO +314 -0
  3. ulpp-1.3.1/README.md +292 -0
  4. ulpp-1.3.1/pyproject.toml +47 -0
  5. ulpp-1.3.1/setup.cfg +4 -0
  6. ulpp-1.3.1/src/ulpp/__init__.py +10 -0
  7. ulpp-1.3.1/src/ulpp/__main__.py +52 -0
  8. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f00.png +0 -0
  9. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f01.png +0 -0
  10. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f02.png +0 -0
  11. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f03.png +0 -0
  12. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f04.png +0 -0
  13. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f05.png +0 -0
  14. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f06.png +0 -0
  15. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f07.png +0 -0
  16. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f08.png +0 -0
  17. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f09.png +0 -0
  18. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f10.png +0 -0
  19. ulpp-1.3.1/src/ulpp/assets/light/560/tray_f11.png +0 -0
  20. ulpp-1.3.1/src/ulpp/assets/light/560/tray_hold.png +0 -0
  21. ulpp-1.3.1/src/ulpp/assets/light/560/tray_plain.png +0 -0
  22. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f00.png +0 -0
  23. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f01.png +0 -0
  24. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f02.png +0 -0
  25. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f03.png +0 -0
  26. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f04.png +0 -0
  27. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f05.png +0 -0
  28. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f06.png +0 -0
  29. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f07.png +0 -0
  30. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f08.png +0 -0
  31. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f09.png +0 -0
  32. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f10.png +0 -0
  33. ulpp-1.3.1/src/ulpp/assets/light/980/tray_f11.png +0 -0
  34. ulpp-1.3.1/src/ulpp/assets/light/980/tray_hold.png +0 -0
  35. ulpp-1.3.1/src/ulpp/assets/light/980/tray_plain.png +0 -0
  36. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f00.png +0 -0
  37. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f01.png +0 -0
  38. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f02.png +0 -0
  39. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f03.png +0 -0
  40. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f04.png +0 -0
  41. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f05.png +0 -0
  42. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f06.png +0 -0
  43. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f07.png +0 -0
  44. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f08.png +0 -0
  45. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f09.png +0 -0
  46. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f10.png +0 -0
  47. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_f11.png +0 -0
  48. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_hold.png +0 -0
  49. ulpp-1.3.1/src/ulpp/assets/mono/560/tray_plain.png +0 -0
  50. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f00.png +0 -0
  51. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f01.png +0 -0
  52. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f02.png +0 -0
  53. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f03.png +0 -0
  54. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f04.png +0 -0
  55. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f05.png +0 -0
  56. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f06.png +0 -0
  57. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f07.png +0 -0
  58. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f08.png +0 -0
  59. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f09.png +0 -0
  60. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f10.png +0 -0
  61. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_f11.png +0 -0
  62. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_hold.png +0 -0
  63. ulpp-1.3.1/src/ulpp/assets/mono/980/tray_plain.png +0 -0
  64. ulpp-1.3.1/src/ulpp/assets/testpage.pg +0 -0
  65. ulpp-1.3.1/src/ulpp/builder.py +581 -0
  66. ulpp-1.3.1/src/ulpp/calibration.py +215 -0
  67. ulpp-1.3.1/src/ulpp/cli.py +263 -0
  68. ulpp-1.3.1/src/ulpp/config.py +74 -0
  69. ulpp-1.3.1/src/ulpp/gui.py +460 -0
  70. ulpp-1.3.1/src/ulpp/i18n.py +192 -0
  71. ulpp-1.3.1/src/ulpp/imagex.py +51 -0
  72. ulpp-1.3.1/src/ulpp/locales/en_US.json +246 -0
  73. ulpp-1.3.1/src/ulpp/locales/zh_CN.json +246 -0
  74. ulpp-1.3.1/src/ulpp/parser.py +373 -0
  75. ulpp-1.3.1/src/ulpp/partial_gui.py +837 -0
  76. ulpp-1.3.1/src/ulpp/presets.py +122 -0
  77. ulpp-1.3.1/src/ulpp/sheets.py +363 -0
  78. ulpp-1.3.1/src/ulpp/textlabel.py +220 -0
  79. ulpp-1.3.1/src/ulpp/wizard.py +647 -0
  80. ulpp-1.3.1/src/ulpp.egg-info/PKG-INFO +314 -0
  81. ulpp-1.3.1/src/ulpp.egg-info/SOURCES.txt +92 -0
  82. ulpp-1.3.1/src/ulpp.egg-info/dependency_links.txt +1 -0
  83. ulpp-1.3.1/src/ulpp.egg-info/entry_points.txt +2 -0
  84. ulpp-1.3.1/src/ulpp.egg-info/requires.txt +9 -0
  85. ulpp-1.3.1/src/ulpp.egg-info/top_level.txt +1 -0
  86. ulpp-1.3.1/tests/test_calibration.py +92 -0
  87. ulpp-1.3.1/tests/test_config.py +52 -0
  88. ulpp-1.3.1/tests/test_i18n.py +175 -0
  89. ulpp-1.3.1/tests/test_integration.py +99 -0
  90. ulpp-1.3.1/tests/test_parser.py +76 -0
  91. ulpp-1.3.1/tests/test_partial.py +103 -0
  92. ulpp-1.3.1/tests/test_sheets.py +134 -0
  93. ulpp-1.3.1/tests/test_textlabel.py +95 -0
  94. ulpp-1.3.1/tests/test_wizard.py +47 -0
ulpp-1.3.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 label-sheet 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.
ulpp-1.3.1/PKG-INFO ADDED
@@ -0,0 +1,314 @@
1
+ Metadata-Version: 2.4
2
+ Name: ulpp
3
+ Version: 1.3.1
4
+ Summary: 把 PDF 版面里的一块或一段文字,按份数排进 A4 标签纸;支持整版与接着打(上次没用完的胶纸),中英双语
5
+ Author: ulpp contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Imensama/ulpp
8
+ Project-URL: Repository, https://github.com/Imensama/ulpp
9
+ Project-URL: Issues, https://github.com/Imensama/ulpp/issues
10
+ Keywords: pdf,label,sticker,a4,printing,layout
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pdfplumber<1.0,>=0.11
15
+ Requires-Dist: pypdf>=4.0
16
+ Requires-Dist: Pillow>=9
17
+ Provides-Extra: gui
18
+ Requires-Dist: tkinterdnd2>=0.3; extra == "gui"
19
+ Provides-Extra: dev
20
+ Requires-Dist: pyinstaller>=6; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # 通用标签打印平台(ulpp)
24
+
25
+ 简体中文 | [English](README.en.md)
26
+
27
+ 把 **PDF 页面里的若干小块**,或**一段文字**,按份数排进 A4 预切不干胶的格子里,输出可直接打印的 PDF。
28
+
29
+ 两种输入:
30
+
31
+ 1. **表格型 PDF** —— 页面本身是“一格一条记录”的表格。工具会按文字锚点找出每一格,
32
+ 把这一格的内容原样抠出来、等比缩放进标签格,内容保持矢量(不是截图)。
33
+ 2. **一段文字** —— 一行一张,自动折行、自动选字号,渲染成 600dpi 位图。
34
+
35
+ 内置 14 种常见 A4 标签纸规格,也可自定义列数、格宽、节距、边距。
36
+ 界面支持中文 / English,命令行 `--lang zh|en`,默认跟随系统(系统语言不是中英时用英文);
37
+ Windows 下双击 exe 就能用,也能命令行批量跑。
38
+ 从 v1.3.0 起还支持**接着打**:上次没用完的胶纸,记住哪些格子空了,只打空格,不用攒满一整版。
39
+
40
+ ![接着打窗口](docs/example-continue.png)
41
+
42
+ > 最初是为了摆脱琐碎手工排版的“案牍之劳形”。
43
+ > 后来加了文字模式和胶贴规格。
44
+
45
+ ![表格型 PDF 的效果](docs/example.png)
46
+ ![文字标签的效果](docs/example-text.png)
47
+
48
+ ## 功能
49
+
50
+ - **按份数复制**:格子里读到“数量 2”就出 2 张,“数量 10”出 10 张,同一张连排,方便分发
51
+ - **矢量裁剪**:整格内容按矢量缩放入格,放大不糊;文字标签用 600dpi 位图,不内嵌字体,PDF 只有几十 KB
52
+ - **文字自动排版**:自动折行、自动选字号;对齐可选居中/左/右;可整批“统一字号”。
53
+ 行尾写 `|2` 或 ` ×2` 表示这张印 2 份(`120×80×6` 这类尺寸不会被误判)
54
+ - **两种内容可混排**:PDF 卡片在前,文字标签接在后面,一起填满一版
55
+ - **一次出三个文件**:打印版(无网格线)、网格参考版(浅灰网格线,试打对位)、标签清单 CSV
56
+ - **内置常见规格**:下拉选中就把列数/格宽/节距/边距填好,再按实际胶纸微调
57
+ - **中英双语**:界面右下角可即时切换,或命令行 `--lang zh|en`;规格名、按钮、提示、输出文件名都会跟着变
58
+ - **位置校准向导**:不用懂“正面朝下·短边朝前”这种术语,看图回答两个问题(字在哪一面 / TOP 箭头在哪条边),
59
+ 自动推出这台机器该怎么放纸,以后自动带出
60
+ - **接着打**:上次没用完的胶纸也能继续用;图形化看到哪些格子空了,点格放置或自动填满,生成单页 PDF;带普通纸校准页和左右/上下微调
61
+ - **输入支持 PDF / 文件夹 / ZIP**(中文文件名的 ZIP 会自动纠正编码)
62
+
63
+ ## PDF 卡片模式是怎么认的
64
+
65
+ - 以“文件名称”这类字段当**锚点**找卡片;份数读“数量”字段
66
+ - 字段名写在 `src/ulpp/parser.py` 顶部(`FIELD_LABELS` 和几个正则里),
67
+ 换一套模板时改这几个常量即可,不用动排版逻辑
68
+ - 一页有几列几行**不写死**,按锚点位置聚类推断;表格线会被识别并剔除,不会跟着缩进标签
69
+ - 一格里没有锚点时,这张卡片会被跳过,并在日志里提示
70
+
71
+ ## 内置标签纸规格
72
+
73
+ | 预设 | 列×行 | 单格 |
74
+ | --- | --- | --- |
75
+ | 45 格(实测量过的规格) | 5×9 | 40 × 30 mm,节距 41.5×32 |
76
+ | 24 格 | 3×8 | 70 × 37 mm |
77
+ | 21 格 | 3×7 | 70 × 42.3 mm |
78
+ | 30 格 | 3×10 | 70 × 29.7 mm |
79
+ | 12 格 | 2×6 | 99 × 49.5 mm |
80
+ | 10 格 | 2×5 | 99 × 59.4 mm |
81
+ | 8 格 | 2×4 | 99 × 74.25 mm |
82
+ | 6 格 | 2×3 | 99 × 99 mm |
83
+ | 4 格 | 2×2 | 105 × 148.5 mm |
84
+ | 2 格 | 1×2 | 210 × 148.5 mm |
85
+ | 40 格 | 4×10 | 48 × 25 mm |
86
+ | 72 格 | 6×12 | 33 × 23 mm |
87
+ | 65 格 | 5×13 | 38 × 22 mm |
88
+ | 44 格 | 4×11 | 46 × 25 mm |
89
+
90
+ > 每家标签纸的裁切线会差 0.5~1mm。选中预设后先打一张“网格参考版”,跟实际胶纸透光叠对;
91
+ > 不合适就改“行节距 / 上边距 / 安全边”三个数字重出。
92
+ >
93
+ > 命令行里也可以用稳定 id:`--preset 45`、`--preset 24`;英文名和旧版中文名同样能用。
94
+
95
+ ## 三种用法
96
+
97
+ ### 1. 免安装:下载 exe
98
+
99
+ 去 [Releases](../../releases) 下载 `ulpp.exe`,双击运行,不用装 Python。
100
+
101
+ > 未签名的 exe 可能被 Windows Defender / SmartScreen 拦一下:点“更多信息 → 仍要运行”。
102
+ > 介意的话用下面的源码方式,效果完全一样。
103
+
104
+ ### 2. 源码运行
105
+
106
+ ```bash
107
+ git clone https://github.com/<your-name>/ulpp.git
108
+ cd ulpp
109
+ pip install -r requirements.txt
110
+ pip install -e . # 让 python -m ulpp 找到 src 下的包
111
+ python run_gui.pyw # Windows 图形界面
112
+ python -m ulpp # 各平台通用(无参数即开界面)
113
+ ```
114
+
115
+ > 不想 `pip install -e .` 的话,在 PowerShell 里 `$env:PYTHONPATH="src"` 也能跑。
116
+
117
+ ### 3. 命令行(适合批量 / 定时)
118
+
119
+ > 以下命令在仓库根目录执行;源码运行时先 `pip install -e .`(或 `$env:PYTHONPATH="src"`)。
120
+
121
+ ```bash
122
+ # 表格型 PDF
123
+ python -m ulpp 输入.zip -o 输出目录
124
+ python -m ulpp 某个目录 --first-page-only --no-proof
125
+ python -m ulpp 表单.pdf --preset 24
126
+
127
+ # 纯文字(一行一张,|2 = 印两份)
128
+ python -m ulpp --text "急件 A-01 |2" --text "返修件" -o 输出目录
129
+ python -m ulpp --text-file 清单.txt --text-uniform
130
+
131
+ # 英文界面(--lang en;不加则跟随系统)
132
+ python -m ulpp --lang en --text "URGENT A-01 |2" --text "REWORK" -o 输出目录
133
+ python -m ulpp --lang en --list-presets
134
+
135
+ # 两者混排
136
+ python -m ulpp 表单.pdf --text "加急 |2" --preset 45
137
+
138
+ python -m ulpp --list-presets # 看所有规格
139
+ python -m ulpp --save-config g.json # 存一套参数,下次 --config g.json
140
+ ```
141
+
142
+ `python -m ulpp -h` 看全部参数。`--preset` 可以用稳定 id(`45`、`24`…)、英文名,或旧版中文名,
143
+ 老脚本不用改。
144
+
145
+ ## 界面怎么用
146
+
147
+ 1. **① 添加 PDF**(可跳过):点“添加 PDF / 文件夹 / 压缩包”,装了 `tkinterdnd2` 也能直接拖进列表
148
+ 2. **② 文字标签**(可跳过):一行一张,行尾 `|2` 表示两份;可选对齐和“统一字号”
149
+ 3. **③ 生成选项**:全部页 / 仅第 1 页;是否出网格参考版和清单;输出目录
150
+ 4. **④ 标签纸规格与版式参数**:先选规格,尺寸自动填好,再按实际裁切线微调
151
+ 5. 点 **开始生成**,完成后点“打开输出文件夹”
152
+ 6. 右下角 **语言 / Language** 下拉可在“中文 / English”之间即时切换,文件列表、文字和参数都会保留;
153
+ 不选时按 `--lang` > 环境变量 `LC_ALL`/`LANG` > 系统语言;系统语言不是中文/英文时默认英文
154
+ 7. 右下角 **接着打…** 打开没用完的胶纸窗口;整版打印流程不变
155
+
156
+ ## 位置校准向导(v1.3.1)
157
+
158
+ “正着放还是反着放、哪条边先伸进去”,这件事每台机器都不一样,说明书上还常常写反。
159
+ 向导把它变成两个不用懂打印机就能答的问题。
160
+
161
+ 入口:**接着打** 窗口 → 位置校准与放纸 → **不知道怎么放?用校准向导…**
162
+ (旁边的“放纸方向”下拉框保留,已经知道答案的人可以直接选。)
163
+
164
+ ![位置校准向导](docs/wizard.png)
165
+
166
+ 九步:选机器 → 写个大 A → 确认 A 面朝上 → 放上手送盘(带放纸动画)→
167
+ 打一张方向测试页(普通纸)→ 问“字在哪一面” → 问“TOP 箭头在哪条边” → 结论 → 左右/上下微调。
168
+
169
+ ![向导结论](docs/wizard-done.png)
170
+
171
+ - 测试页是 **A4 300dpi 普通纸**,带 TOP 箭头、大 A、粗黑条,翻到背面也看得出哪面着墨;
172
+ 打印时必须 **100% 实际大小**,不要用“适应页面”
173
+ - 结果写进这台机器/纸盘的校准配置(就是下拉框那四个值),下次自动带出
174
+ - 支持中文 / English,两套皮肤(浅色 / 黑白)
175
+
176
+ > 它只解决**方向**,不解决**标签尺寸**。纸张的实际切割位置要靠扫描量(见“限制”)。
177
+
178
+ ## 接着打(上次没用完的胶纸)
179
+
180
+ 主界面右下角点 **接着打…**,会打开一个独立窗口(整版打印流程不受影响)。
181
+
182
+ 1. **新建一张没用完的胶纸**:选内置规格,或选“自定义”填列数/行数/格宽/节距(比如 3×4)。
183
+ 2. **标已用格**:用过的格子右键点一下变灰;点错了再右键恢复。
184
+ 3. **放内容**:左边加 PDF 卡片或粘贴文字;选中一条内容,左键点空格=“打在这儿”;也可以点“自动填满空格”。
185
+ 4. **生成打印 PDF**:只包含选中的格子,其他位置留白;打印时选 **实际大小 100%**,一次放一张。
186
+ 5. **打印后标记已用**:点“已打印,标记已用”,这些格子变灰;点错了点“撤销上一单”。
187
+ 6. **位置校准**:点“打印校准页…”生成一张**普通纸**参考页,和没用完的胶纸对比格子位置;
188
+ 整体偏右/偏下多少 mm,就在左右/上下微调里填对应的负数。校准页不会印在胶纸上,也不会污染标签。
189
+
190
+ 记录存在 `%APPDATA%\ulpp\sheets.json`(Windows)或 `~/.config/ulpp/sheets.json`;
191
+ 旧版 `inventory.json` 会自动迁移。
192
+
193
+ 限制(v1.3.1):
194
+
195
+ - **内置规格只能当起点**:A4 不干胶各厂各批的切割尺寸都不一样(实测同一款 45 格纸,
196
+ 标称节距 41.5 mm,量出来是 42.26 mm —— 打到第 5 列就偏大约 3 mm)。
197
+ 目前靠“网格参考版 + 左右/上下微调”手工对齐;下一步用**扫描识别**一次量准(不需要联网、不依赖 AI)
198
+ - 空格需要人工维护一次;扫描自动识别格子还在规划里
199
+ - 只能打**标签还贴着**的格子;已经撕掉标签的格子标成已用,不要打(硅油衬纸不着墨)
200
+ - 消费级打印机重复放纸有 ±1~2mm 误差;请先用校准页 + 一张试打
201
+ - 激光优先;喷墨在用过的不干胶纸上更容易蹭墨/卷曲,建议先试
202
+ - 暂不支持 A3;纸张尺寸已留在数据结构里,后续可加
203
+
204
+ ## 输出
205
+
206
+ | 文件 | 用途 |
207
+ | --- | --- |
208
+ | `*_打印版.pdf` | 直接上胶纸打印(无网格线) |
209
+ | `*_网格参考版.pdf` | 普通纸试打,和胶纸透光叠对,检查格子对位 |
210
+ | `*_标签清单.csv` | 每张标签在第几版第几格、来源(PDF/文字)、名称、数量、第几份 |
211
+ | `*_接着打.pdf` | 只打没用完的胶纸上的空格 |
212
+ | `*_接着打清单.csv` | 接着打的格子清单 |
213
+
214
+ > 文件名也跟着界面语言走:中文界面是 `*_打印版.pdf`、`*_网格参考版.pdf`、`*_标签清单.csv`、
215
+ > `*_接着打.pdf`、`*_接着打清单.csv`;英文界面是 `*_print.pdf`、`*_grid-reference.pdf`、
216
+ > `*_label-list.csv`、`*_continue.pdf`、`*_continue-list.csv`。默认前缀 `标签` / `label`,
217
+ > 默认输出目录 `标签输出` / `label-output`。
218
+
219
+ ## 目录结构
220
+
221
+ ```
222
+ ulpp/
223
+ ├─ src/ulpp/
224
+ │ ├─ config.py # 版式参数(GridSpec)
225
+ │ ├─ presets.py # 常见 A4 标签纸规格
226
+ │ ├─ parser.py # 解析 PDF:找卡片、读字段、算内容框
227
+ │ ├─ textlabel.py # 文字排版:切词、折行、自动选字号、渲染位图
228
+ │ ├─ imagex.py # 把位图写成 PDF 图像 XObject(1bit + Flate 压缩)
229
+ │ ├─ builder.py # 拼版输出:Form XObject / 图像 + 裁剪 + 缩放
230
+ │ ├─ sheets.py # 没用完的胶纸记录:已用格、位置校准配置
231
+ │ ├─ calibration.py # 普通纸校准页
232
+ │ ├─ partial_gui.py # 接着打窗口(网格、放置、标记已用)
233
+ │ ├─ i18n.py # 运行时多语言:JSON 语言包、系统语言识别、tr()
234
+ │ ├─ locales/ # zh_CN.json / en_US.json
235
+ │ ├─ cli.py # 命令行
236
+ │ ├─ gui.py # 图形界面(tkinter)
237
+ │ └─ __main__.py # 有参数走 CLI,无参数开界面
238
+ ├─ packaging/ # PyInstaller 打包配置与脚本
239
+ ├─ tests/ # 单元测试
240
+ └─ .github/workflows/build-windows.yml # 打 tag 自动打包并发 Release
241
+ ```
242
+
243
+ ## 技术要点
244
+
245
+ - 解析用 **pdfplumber**(拿文字和线条坐标):以字段名所在的词当锚点,按最近的列起点归格,
246
+ 同一格的词按 top 聚成行,从锚点行开始按字段名收行;遇到下一个“文件名称”就停。
247
+ - PDF 输出用 **pypdf**:源页做成 **Form XObject**(整页只内嵌一份),用「裁剪路径 + 缩放矩阵 + `Do`」
248
+ 画进小格。**裁剪框必须用内容区而不是整格**,否则安全边里会漏出相邻内容。
249
+ - 文字输出用 **Pillow** 渲染成 600dpi 1bit 图,写成 `/Image` XObject(Flate 压缩)。
250
+ 不内嵌中文字体:标签很小,位图印出来和矢量几乎看不出差别,PDF 也不会膨胀十几 MB。
251
+ - 表格线识别:细长且贯通整页的线判为格线剔除,避免把表格边框缩进标签。
252
+ - 多语言:两份 JSON 语言包,测试会校验 key 集合完全一致、占位符一致,并检查代码里用到的
253
+ `tr()` key 都存在;语言包缺失或缺 key 时回退中文并在日志里警告,不中断运行。
254
+ - 接着打:`build_partial_pdf()` 只把指定格号画到一页上;左右/上下微调把格子整体平移后再裁剪,
255
+ 保证内容仍落在标签内。胶纸记录是本地 JSON,打印后由用户确认标记已用,支持撤销。
256
+
257
+ ## 开发与打包
258
+
259
+ ```bash
260
+ pip install -r requirements.txt
261
+ pip install -e .
262
+ python -m unittest discover -s tests -t . # 73 个用例
263
+ ```
264
+
265
+ ```powershell
266
+ powershell -ExecutionPolicy Bypass -File packaging\build.ps1
267
+ # 产物:dist\ulpp.exe(单文件,约 36MB)
268
+ ```
269
+
270
+ 或者用 GitHub Actions:打 tag(如 `v1.3.0`)后自动构建并上传 Release 附件。
271
+
272
+ ## 平台支持
273
+
274
+ | 平台 | 状态 | 需要额外装 |
275
+ | --- | --- | --- |
276
+ | Windows 10/11 | 主要支持,有 exe | 无 |
277
+ | macOS | 源码可跑 | Python 3.9+(官网安装包自带 tkinter;Homebrew 需 `brew install python-tk`) |
278
+ | Linux | 源码可跑 | `python3-tk`;文字模式需要字体:英文用 DejaVu Sans,中文用 `fonts-wqy-microhei` |
279
+
280
+ 接着打需要打印机支持单张手送/侧供纸;纸盒放没用完的胶纸是否识别取决于机型。
281
+
282
+ exe 是 Windows 专用;mac/Linux 用 `python -m ulpp`。
283
+ “打开输出文件夹”按钮在 mac/Linux 上暂时只会弹提示,不会真的打开文件管理器。
284
+
285
+ ## 常见问题
286
+
287
+ **Q:识别不到卡片?**
288
+ A:这个模式针对“每格一条记录”的表格型 PDF。扫描件(整页图片)或没有这些字段名的 PDF 取不到——
289
+ 那种情况可以用文字模式手工粘,或者改 `parser.py` 顶部的字段名。
290
+
291
+ **Q:文字标签字号忽大忽小?**
292
+ A:默认每张尽量填满;勾上“统一字号”后整批用同一档(自动取放得下的最小值)。
293
+
294
+ **Q:一个字被挤到下一行?**
295
+ A:已经做了“孤字回拉”,极窄的格子里仍可能出现;把文字缩短或减小“安全边”即可。
296
+
297
+ **Q:某个格子的标签空白 / 只有一小条?**
298
+ A:多半是那一格的表格线被判成了内容。把 PDF 发个 issue,或先用 `--inset 1` 试试。
299
+
300
+ **Q:格子位置对不上?**
301
+ A:先打网格参考版,量出偏移,改“上边距”和“行节距”,或换一个更接近的规格预设。
302
+ 接着打时,先在普通纸上打校准页,把左右/上下微调填好再打胶纸。
303
+
304
+ **Q:上次没用完的胶纸怎么接着打?**
305
+ A:点“接着打…”,新建一张没用完的胶纸;右键把已经用过的格子标灰;把这次的内容放进空格;
306
+ 打印后点“已打印,标记已用”。第一次用先打一张普通纸校准页,和不干胶纸对比格子位置。
307
+
308
+ **Q:能加条码 / 二维码吗?**
309
+ A:目前不生成;源 PDF 里已有的会被原样抠过来。需要生成可以基于 `textlabel.py` 扩展。
310
+
311
+ ## 许可
312
+
313
+ [MIT](LICENSE)。第三方组件均为宽松协议(MIT / BSD / PSF / MIT-CMU),可以随 exe 分发到 Releases。
314
+ 详见 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)。
ulpp-1.3.1/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # 通用标签打印平台(ulpp)
2
+
3
+ 简体中文 | [English](README.en.md)
4
+
5
+ 把 **PDF 页面里的若干小块**,或**一段文字**,按份数排进 A4 预切不干胶的格子里,输出可直接打印的 PDF。
6
+
7
+ 两种输入:
8
+
9
+ 1. **表格型 PDF** —— 页面本身是“一格一条记录”的表格。工具会按文字锚点找出每一格,
10
+ 把这一格的内容原样抠出来、等比缩放进标签格,内容保持矢量(不是截图)。
11
+ 2. **一段文字** —— 一行一张,自动折行、自动选字号,渲染成 600dpi 位图。
12
+
13
+ 内置 14 种常见 A4 标签纸规格,也可自定义列数、格宽、节距、边距。
14
+ 界面支持中文 / English,命令行 `--lang zh|en`,默认跟随系统(系统语言不是中英时用英文);
15
+ Windows 下双击 exe 就能用,也能命令行批量跑。
16
+ 从 v1.3.0 起还支持**接着打**:上次没用完的胶纸,记住哪些格子空了,只打空格,不用攒满一整版。
17
+
18
+ ![接着打窗口](docs/example-continue.png)
19
+
20
+ > 最初是为了摆脱琐碎手工排版的“案牍之劳形”。
21
+ > 后来加了文字模式和胶贴规格。
22
+
23
+ ![表格型 PDF 的效果](docs/example.png)
24
+ ![文字标签的效果](docs/example-text.png)
25
+
26
+ ## 功能
27
+
28
+ - **按份数复制**:格子里读到“数量 2”就出 2 张,“数量 10”出 10 张,同一张连排,方便分发
29
+ - **矢量裁剪**:整格内容按矢量缩放入格,放大不糊;文字标签用 600dpi 位图,不内嵌字体,PDF 只有几十 KB
30
+ - **文字自动排版**:自动折行、自动选字号;对齐可选居中/左/右;可整批“统一字号”。
31
+ 行尾写 `|2` 或 ` ×2` 表示这张印 2 份(`120×80×6` 这类尺寸不会被误判)
32
+ - **两种内容可混排**:PDF 卡片在前,文字标签接在后面,一起填满一版
33
+ - **一次出三个文件**:打印版(无网格线)、网格参考版(浅灰网格线,试打对位)、标签清单 CSV
34
+ - **内置常见规格**:下拉选中就把列数/格宽/节距/边距填好,再按实际胶纸微调
35
+ - **中英双语**:界面右下角可即时切换,或命令行 `--lang zh|en`;规格名、按钮、提示、输出文件名都会跟着变
36
+ - **位置校准向导**:不用懂“正面朝下·短边朝前”这种术语,看图回答两个问题(字在哪一面 / TOP 箭头在哪条边),
37
+ 自动推出这台机器该怎么放纸,以后自动带出
38
+ - **接着打**:上次没用完的胶纸也能继续用;图形化看到哪些格子空了,点格放置或自动填满,生成单页 PDF;带普通纸校准页和左右/上下微调
39
+ - **输入支持 PDF / 文件夹 / ZIP**(中文文件名的 ZIP 会自动纠正编码)
40
+
41
+ ## PDF 卡片模式是怎么认的
42
+
43
+ - 以“文件名称”这类字段当**锚点**找卡片;份数读“数量”字段
44
+ - 字段名写在 `src/ulpp/parser.py` 顶部(`FIELD_LABELS` 和几个正则里),
45
+ 换一套模板时改这几个常量即可,不用动排版逻辑
46
+ - 一页有几列几行**不写死**,按锚点位置聚类推断;表格线会被识别并剔除,不会跟着缩进标签
47
+ - 一格里没有锚点时,这张卡片会被跳过,并在日志里提示
48
+
49
+ ## 内置标签纸规格
50
+
51
+ | 预设 | 列×行 | 单格 |
52
+ | --- | --- | --- |
53
+ | 45 格(实测量过的规格) | 5×9 | 40 × 30 mm,节距 41.5×32 |
54
+ | 24 格 | 3×8 | 70 × 37 mm |
55
+ | 21 格 | 3×7 | 70 × 42.3 mm |
56
+ | 30 格 | 3×10 | 70 × 29.7 mm |
57
+ | 12 格 | 2×6 | 99 × 49.5 mm |
58
+ | 10 格 | 2×5 | 99 × 59.4 mm |
59
+ | 8 格 | 2×4 | 99 × 74.25 mm |
60
+ | 6 格 | 2×3 | 99 × 99 mm |
61
+ | 4 格 | 2×2 | 105 × 148.5 mm |
62
+ | 2 格 | 1×2 | 210 × 148.5 mm |
63
+ | 40 格 | 4×10 | 48 × 25 mm |
64
+ | 72 格 | 6×12 | 33 × 23 mm |
65
+ | 65 格 | 5×13 | 38 × 22 mm |
66
+ | 44 格 | 4×11 | 46 × 25 mm |
67
+
68
+ > 每家标签纸的裁切线会差 0.5~1mm。选中预设后先打一张“网格参考版”,跟实际胶纸透光叠对;
69
+ > 不合适就改“行节距 / 上边距 / 安全边”三个数字重出。
70
+ >
71
+ > 命令行里也可以用稳定 id:`--preset 45`、`--preset 24`;英文名和旧版中文名同样能用。
72
+
73
+ ## 三种用法
74
+
75
+ ### 1. 免安装:下载 exe
76
+
77
+ 去 [Releases](../../releases) 下载 `ulpp.exe`,双击运行,不用装 Python。
78
+
79
+ > 未签名的 exe 可能被 Windows Defender / SmartScreen 拦一下:点“更多信息 → 仍要运行”。
80
+ > 介意的话用下面的源码方式,效果完全一样。
81
+
82
+ ### 2. 源码运行
83
+
84
+ ```bash
85
+ git clone https://github.com/<your-name>/ulpp.git
86
+ cd ulpp
87
+ pip install -r requirements.txt
88
+ pip install -e . # 让 python -m ulpp 找到 src 下的包
89
+ python run_gui.pyw # Windows 图形界面
90
+ python -m ulpp # 各平台通用(无参数即开界面)
91
+ ```
92
+
93
+ > 不想 `pip install -e .` 的话,在 PowerShell 里 `$env:PYTHONPATH="src"` 也能跑。
94
+
95
+ ### 3. 命令行(适合批量 / 定时)
96
+
97
+ > 以下命令在仓库根目录执行;源码运行时先 `pip install -e .`(或 `$env:PYTHONPATH="src"`)。
98
+
99
+ ```bash
100
+ # 表格型 PDF
101
+ python -m ulpp 输入.zip -o 输出目录
102
+ python -m ulpp 某个目录 --first-page-only --no-proof
103
+ python -m ulpp 表单.pdf --preset 24
104
+
105
+ # 纯文字(一行一张,|2 = 印两份)
106
+ python -m ulpp --text "急件 A-01 |2" --text "返修件" -o 输出目录
107
+ python -m ulpp --text-file 清单.txt --text-uniform
108
+
109
+ # 英文界面(--lang en;不加则跟随系统)
110
+ python -m ulpp --lang en --text "URGENT A-01 |2" --text "REWORK" -o 输出目录
111
+ python -m ulpp --lang en --list-presets
112
+
113
+ # 两者混排
114
+ python -m ulpp 表单.pdf --text "加急 |2" --preset 45
115
+
116
+ python -m ulpp --list-presets # 看所有规格
117
+ python -m ulpp --save-config g.json # 存一套参数,下次 --config g.json
118
+ ```
119
+
120
+ `python -m ulpp -h` 看全部参数。`--preset` 可以用稳定 id(`45`、`24`…)、英文名,或旧版中文名,
121
+ 老脚本不用改。
122
+
123
+ ## 界面怎么用
124
+
125
+ 1. **① 添加 PDF**(可跳过):点“添加 PDF / 文件夹 / 压缩包”,装了 `tkinterdnd2` 也能直接拖进列表
126
+ 2. **② 文字标签**(可跳过):一行一张,行尾 `|2` 表示两份;可选对齐和“统一字号”
127
+ 3. **③ 生成选项**:全部页 / 仅第 1 页;是否出网格参考版和清单;输出目录
128
+ 4. **④ 标签纸规格与版式参数**:先选规格,尺寸自动填好,再按实际裁切线微调
129
+ 5. 点 **开始生成**,完成后点“打开输出文件夹”
130
+ 6. 右下角 **语言 / Language** 下拉可在“中文 / English”之间即时切换,文件列表、文字和参数都会保留;
131
+ 不选时按 `--lang` > 环境变量 `LC_ALL`/`LANG` > 系统语言;系统语言不是中文/英文时默认英文
132
+ 7. 右下角 **接着打…** 打开没用完的胶纸窗口;整版打印流程不变
133
+
134
+ ## 位置校准向导(v1.3.1)
135
+
136
+ “正着放还是反着放、哪条边先伸进去”,这件事每台机器都不一样,说明书上还常常写反。
137
+ 向导把它变成两个不用懂打印机就能答的问题。
138
+
139
+ 入口:**接着打** 窗口 → 位置校准与放纸 → **不知道怎么放?用校准向导…**
140
+ (旁边的“放纸方向”下拉框保留,已经知道答案的人可以直接选。)
141
+
142
+ ![位置校准向导](docs/wizard.png)
143
+
144
+ 九步:选机器 → 写个大 A → 确认 A 面朝上 → 放上手送盘(带放纸动画)→
145
+ 打一张方向测试页(普通纸)→ 问“字在哪一面” → 问“TOP 箭头在哪条边” → 结论 → 左右/上下微调。
146
+
147
+ ![向导结论](docs/wizard-done.png)
148
+
149
+ - 测试页是 **A4 300dpi 普通纸**,带 TOP 箭头、大 A、粗黑条,翻到背面也看得出哪面着墨;
150
+ 打印时必须 **100% 实际大小**,不要用“适应页面”
151
+ - 结果写进这台机器/纸盘的校准配置(就是下拉框那四个值),下次自动带出
152
+ - 支持中文 / English,两套皮肤(浅色 / 黑白)
153
+
154
+ > 它只解决**方向**,不解决**标签尺寸**。纸张的实际切割位置要靠扫描量(见“限制”)。
155
+
156
+ ## 接着打(上次没用完的胶纸)
157
+
158
+ 主界面右下角点 **接着打…**,会打开一个独立窗口(整版打印流程不受影响)。
159
+
160
+ 1. **新建一张没用完的胶纸**:选内置规格,或选“自定义”填列数/行数/格宽/节距(比如 3×4)。
161
+ 2. **标已用格**:用过的格子右键点一下变灰;点错了再右键恢复。
162
+ 3. **放内容**:左边加 PDF 卡片或粘贴文字;选中一条内容,左键点空格=“打在这儿”;也可以点“自动填满空格”。
163
+ 4. **生成打印 PDF**:只包含选中的格子,其他位置留白;打印时选 **实际大小 100%**,一次放一张。
164
+ 5. **打印后标记已用**:点“已打印,标记已用”,这些格子变灰;点错了点“撤销上一单”。
165
+ 6. **位置校准**:点“打印校准页…”生成一张**普通纸**参考页,和没用完的胶纸对比格子位置;
166
+ 整体偏右/偏下多少 mm,就在左右/上下微调里填对应的负数。校准页不会印在胶纸上,也不会污染标签。
167
+
168
+ 记录存在 `%APPDATA%\ulpp\sheets.json`(Windows)或 `~/.config/ulpp/sheets.json`;
169
+ 旧版 `inventory.json` 会自动迁移。
170
+
171
+ 限制(v1.3.1):
172
+
173
+ - **内置规格只能当起点**:A4 不干胶各厂各批的切割尺寸都不一样(实测同一款 45 格纸,
174
+ 标称节距 41.5 mm,量出来是 42.26 mm —— 打到第 5 列就偏大约 3 mm)。
175
+ 目前靠“网格参考版 + 左右/上下微调”手工对齐;下一步用**扫描识别**一次量准(不需要联网、不依赖 AI)
176
+ - 空格需要人工维护一次;扫描自动识别格子还在规划里
177
+ - 只能打**标签还贴着**的格子;已经撕掉标签的格子标成已用,不要打(硅油衬纸不着墨)
178
+ - 消费级打印机重复放纸有 ±1~2mm 误差;请先用校准页 + 一张试打
179
+ - 激光优先;喷墨在用过的不干胶纸上更容易蹭墨/卷曲,建议先试
180
+ - 暂不支持 A3;纸张尺寸已留在数据结构里,后续可加
181
+
182
+ ## 输出
183
+
184
+ | 文件 | 用途 |
185
+ | --- | --- |
186
+ | `*_打印版.pdf` | 直接上胶纸打印(无网格线) |
187
+ | `*_网格参考版.pdf` | 普通纸试打,和胶纸透光叠对,检查格子对位 |
188
+ | `*_标签清单.csv` | 每张标签在第几版第几格、来源(PDF/文字)、名称、数量、第几份 |
189
+ | `*_接着打.pdf` | 只打没用完的胶纸上的空格 |
190
+ | `*_接着打清单.csv` | 接着打的格子清单 |
191
+
192
+ > 文件名也跟着界面语言走:中文界面是 `*_打印版.pdf`、`*_网格参考版.pdf`、`*_标签清单.csv`、
193
+ > `*_接着打.pdf`、`*_接着打清单.csv`;英文界面是 `*_print.pdf`、`*_grid-reference.pdf`、
194
+ > `*_label-list.csv`、`*_continue.pdf`、`*_continue-list.csv`。默认前缀 `标签` / `label`,
195
+ > 默认输出目录 `标签输出` / `label-output`。
196
+
197
+ ## 目录结构
198
+
199
+ ```
200
+ ulpp/
201
+ ├─ src/ulpp/
202
+ │ ├─ config.py # 版式参数(GridSpec)
203
+ │ ├─ presets.py # 常见 A4 标签纸规格
204
+ │ ├─ parser.py # 解析 PDF:找卡片、读字段、算内容框
205
+ │ ├─ textlabel.py # 文字排版:切词、折行、自动选字号、渲染位图
206
+ │ ├─ imagex.py # 把位图写成 PDF 图像 XObject(1bit + Flate 压缩)
207
+ │ ├─ builder.py # 拼版输出:Form XObject / 图像 + 裁剪 + 缩放
208
+ │ ├─ sheets.py # 没用完的胶纸记录:已用格、位置校准配置
209
+ │ ├─ calibration.py # 普通纸校准页
210
+ │ ├─ partial_gui.py # 接着打窗口(网格、放置、标记已用)
211
+ │ ├─ i18n.py # 运行时多语言:JSON 语言包、系统语言识别、tr()
212
+ │ ├─ locales/ # zh_CN.json / en_US.json
213
+ │ ├─ cli.py # 命令行
214
+ │ ├─ gui.py # 图形界面(tkinter)
215
+ │ └─ __main__.py # 有参数走 CLI,无参数开界面
216
+ ├─ packaging/ # PyInstaller 打包配置与脚本
217
+ ├─ tests/ # 单元测试
218
+ └─ .github/workflows/build-windows.yml # 打 tag 自动打包并发 Release
219
+ ```
220
+
221
+ ## 技术要点
222
+
223
+ - 解析用 **pdfplumber**(拿文字和线条坐标):以字段名所在的词当锚点,按最近的列起点归格,
224
+ 同一格的词按 top 聚成行,从锚点行开始按字段名收行;遇到下一个“文件名称”就停。
225
+ - PDF 输出用 **pypdf**:源页做成 **Form XObject**(整页只内嵌一份),用「裁剪路径 + 缩放矩阵 + `Do`」
226
+ 画进小格。**裁剪框必须用内容区而不是整格**,否则安全边里会漏出相邻内容。
227
+ - 文字输出用 **Pillow** 渲染成 600dpi 1bit 图,写成 `/Image` XObject(Flate 压缩)。
228
+ 不内嵌中文字体:标签很小,位图印出来和矢量几乎看不出差别,PDF 也不会膨胀十几 MB。
229
+ - 表格线识别:细长且贯通整页的线判为格线剔除,避免把表格边框缩进标签。
230
+ - 多语言:两份 JSON 语言包,测试会校验 key 集合完全一致、占位符一致,并检查代码里用到的
231
+ `tr()` key 都存在;语言包缺失或缺 key 时回退中文并在日志里警告,不中断运行。
232
+ - 接着打:`build_partial_pdf()` 只把指定格号画到一页上;左右/上下微调把格子整体平移后再裁剪,
233
+ 保证内容仍落在标签内。胶纸记录是本地 JSON,打印后由用户确认标记已用,支持撤销。
234
+
235
+ ## 开发与打包
236
+
237
+ ```bash
238
+ pip install -r requirements.txt
239
+ pip install -e .
240
+ python -m unittest discover -s tests -t . # 73 个用例
241
+ ```
242
+
243
+ ```powershell
244
+ powershell -ExecutionPolicy Bypass -File packaging\build.ps1
245
+ # 产物:dist\ulpp.exe(单文件,约 36MB)
246
+ ```
247
+
248
+ 或者用 GitHub Actions:打 tag(如 `v1.3.0`)后自动构建并上传 Release 附件。
249
+
250
+ ## 平台支持
251
+
252
+ | 平台 | 状态 | 需要额外装 |
253
+ | --- | --- | --- |
254
+ | Windows 10/11 | 主要支持,有 exe | 无 |
255
+ | macOS | 源码可跑 | Python 3.9+(官网安装包自带 tkinter;Homebrew 需 `brew install python-tk`) |
256
+ | Linux | 源码可跑 | `python3-tk`;文字模式需要字体:英文用 DejaVu Sans,中文用 `fonts-wqy-microhei` |
257
+
258
+ 接着打需要打印机支持单张手送/侧供纸;纸盒放没用完的胶纸是否识别取决于机型。
259
+
260
+ exe 是 Windows 专用;mac/Linux 用 `python -m ulpp`。
261
+ “打开输出文件夹”按钮在 mac/Linux 上暂时只会弹提示,不会真的打开文件管理器。
262
+
263
+ ## 常见问题
264
+
265
+ **Q:识别不到卡片?**
266
+ A:这个模式针对“每格一条记录”的表格型 PDF。扫描件(整页图片)或没有这些字段名的 PDF 取不到——
267
+ 那种情况可以用文字模式手工粘,或者改 `parser.py` 顶部的字段名。
268
+
269
+ **Q:文字标签字号忽大忽小?**
270
+ A:默认每张尽量填满;勾上“统一字号”后整批用同一档(自动取放得下的最小值)。
271
+
272
+ **Q:一个字被挤到下一行?**
273
+ A:已经做了“孤字回拉”,极窄的格子里仍可能出现;把文字缩短或减小“安全边”即可。
274
+
275
+ **Q:某个格子的标签空白 / 只有一小条?**
276
+ A:多半是那一格的表格线被判成了内容。把 PDF 发个 issue,或先用 `--inset 1` 试试。
277
+
278
+ **Q:格子位置对不上?**
279
+ A:先打网格参考版,量出偏移,改“上边距”和“行节距”,或换一个更接近的规格预设。
280
+ 接着打时,先在普通纸上打校准页,把左右/上下微调填好再打胶纸。
281
+
282
+ **Q:上次没用完的胶纸怎么接着打?**
283
+ A:点“接着打…”,新建一张没用完的胶纸;右键把已经用过的格子标灰;把这次的内容放进空格;
284
+ 打印后点“已打印,标记已用”。第一次用先打一张普通纸校准页,和不干胶纸对比格子位置。
285
+
286
+ **Q:能加条码 / 二维码吗?**
287
+ A:目前不生成;源 PDF 里已有的会被原样抠过来。需要生成可以基于 `textlabel.py` 扩展。
288
+
289
+ ## 许可
290
+
291
+ [MIT](LICENSE)。第三方组件均为宽松协议(MIT / BSD / PSF / MIT-CMU),可以随 exe 分发到 Releases。
292
+ 详见 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)。
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ulpp"
7
+ version = "1.3.1"
8
+ description = "把 PDF 版面里的一块或一段文字,按份数排进 A4 标签纸;支持整版与接着打(上次没用完的胶纸),中英双语"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "ulpp contributors" }]
14
+ keywords = ["pdf", "label", "sticker", "a4", "printing", "layout"]
15
+ dependencies = [
16
+ "pdfplumber>=0.11,<1.0",
17
+ "pypdf>=4.0",
18
+ "Pillow>=9",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ gui = ["tkinterdnd2>=0.3"] # 想要拖拽入文件可选装
23
+ dev = ["pyinstaller>=6"]
24
+
25
+ [project.scripts]
26
+ ulpp = "ulpp.cli:main"
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/Imensama/ulpp"
30
+ Repository = "https://github.com/Imensama/ulpp"
31
+ Issues = "https://github.com/Imensama/ulpp/issues"
32
+
33
+ [tool.setuptools]
34
+ package-dir = { "" = "src" }
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
38
+
39
+ [tool.setuptools.package-data]
40
+ # 语言包 + 校准向导的插图(560/980 两套分辨率)与测试页原料,缺一不可
41
+ ulpp = [
42
+ "locales/*.json",
43
+ "assets/*.pg",
44
+ "assets/*/*/*.png",
45
+ ]
46
+
47
+