Files
OnebotCatalog/onebot-data/tools/gen-clamp-manual.py
wangruiguo 3c4f236cba v2.0: 4 分类真实数据替换 + 手册彩色重做 + 网页版改版
数据 (定位销/勾销缸/翻转缸/电缸 → Univer 真实数模)
- 新增 19 系列 1124 型号 (PIN 303 / HOK 73 / ROT 741 / ELEC 9)
- 命名规则取自官方图册 CODIFICATION KEY (pdftotext 提取), 非推测
- 解析用「官方值域校验 + 左对齐 DFS 填空」, 可处理省略空位的型号
- 参数组合唯一性门禁: 1124 型号全部通过 (避免选型静默错配)
- 占位/测试文件 (如 USP50J_0___K0 销径为空) 在解析阶段丢弃
- catalog.json 分类系列表同步; crossref 失效引用清理

手册页
- 全部 42 系列改为彩色 (此前脚本误写死 csGRAY 转灰度), 346 页
- 修复 UCBT 附图只有 1 页 (筛选条件写死 UCBT40 且漏 CLAMPING ARMS)
- gen-pending-series.py 改为扫描 manual 目录自动生成附件清单,
  避免重跑脚本把多页附件覆盖回单页

网页版
- 首页改为系列缩略图网格 (40 张: 27 张真实产品图 + 13 张占位图),
  按大类分组; 图放 web/series/<系列码>.webp, 覆盖同名文件即可替换
- 左侧树改手风琴 (默认收起, 一次展开一个), 父级带 SVG 图标,
  悬停放大动画, 按钮间距随高度均匀分布; 可收起为图标窄条
- 修复分隔条被拖出容器后无法找回 (上限改为容器感知 + resize 重夹取
  + 双击复位); 同时阻止图片原生拖拽
- 购物车打包改取 .step.gz 副档 (传输量降至约 1/4.5), 无副档自动回退
- 修复搜索到型号后点结果内容区空白 (switchTab('dim') 已失效)
- OpcPackage 超大包 (>1.5GB) 改走临时文件, 绕过 MemoryStream 2GB 上限

工具
- gen-pending-series/manual, copy-pending-step, gen-step-gz,
  gen-series-thumbs, scan-pending-series 等 11 个脚本

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-15 01:05:41 +08:00

133 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
为夹紧缸 11 个系列生成附图(多页尺寸图 + 裁剪版数据表 PDF)。
来源:Univer 官方图册 PDF(X 盘)。
用法: PYTHONUTF8=1 python gen-clamp-manual.py [--clean]
--clean 先清空各系列 manual 目录再生成(避免旧页残留)
输出: manual/<系列>/p<NN>.png + <系列>_manual.pdf(裁成该系列的页)
"""
import fitz, os, shutil, sys
SRC = r'X:/新开发产品-Research and Development/2_产品资料/Univer资料/官方图册'
DST = r'D:/开发/OnebotCatalog/onebot-data/catalog/manual'
DPI = 110
CLEAN = '--clean' in sys.argv
MAIN_CATALOG = 'katalog-pneumatische-spanner-univer-13032019-en.pdf'
# 系列 -> 官方 PDF 列表(多缸径系列合并成一个系列的图,按缸径顺序)
SERIES_PDFS = {
'UCBP': ['ucbp25cten_en380', 'ucbp40en_en382', 'ucbp50cten_en391', 'ucbp63cten_en401', 'ucbp80cten_en392'],
'UCBM': ['ucbm32cten_en432', 'ucbm50cten_en394', 'ucbm63cten_en395', 'ucbm80cten_en414'],
'UCBH': ['ucbh63cten_en393'],
'UCBQ': ['ucbq63cten_en397'],
'UCDP': ['ucdp40cten_en417', 'ucdp63cten_en418'],
'UCIM': ['ucim40cten_en415'],
'UCIP': ['ucip40cten_en402'],
'UCNM': ['ucnm50cten_en387', 'ucnm63cten_en389', 'ucnm80cten_en429'],
'UCNP': ['ucnp50cten_en385', 'ucnp63cten_en403', 'ucnp80cten_en388'],
# UFL450/UHL463 实为官方 UCBL(Manual power clamps Ø50/Ø63)
'UFL': ['ucbl450cten_en398', 'ucbl463cten_en478'],
}
UCBT_FROM_MAIN = True # UCBT 无独立图册,从主目录提取
def classify(page):
"""按内容标记归类页面: char(特性) / dim(尺寸) / arm(夹臂配件) / other"""
t = page.get_text().upper()
if 'CLAMPING ARMS' in t or 'ACCESSORIES' in t:
return 'arm'
if 'DIMENSIONS' in t:
return 'dim'
if 'CHARACTERISTICS' in t or 'CODIFICATION' in t:
return 'char'
return 'other'
def render(pages, out_dir, start_no):
"""pages = [(doc, page_idx), ...] 渲染为 p<NN>.png,返回下一个编号"""
n = start_no
for doc, i in pages:
# 彩色输出 (官方图册本身是彩色的; 早期版本误用 csGRAY 转灰度)
pix = doc[i].get_pixmap(dpi=DPI)
pix.save(os.path.join(out_dir, f'p{n:02d}.png'))
n += 1
return n
def main():
os.makedirs(DST, exist_ok=True)
for series, pdfs in SERIES_PDFS.items():
d = os.path.join(DST, series)
if CLEAN and os.path.isdir(d):
shutil.rmtree(d)
os.makedirs(d, exist_ok=True)
all_pages = [] # (doc, page_idx, kind)
src_docs = []
for name in pdfs:
p = os.path.join(SRC, name + '.pdf')
if not os.path.exists(p):
print(f'[{series}] 源缺失: {name}')
continue
doc = fitz.open(p)
src_docs.append((name, doc))
for i in range(doc.page_count):
all_pages.append((doc, i, classify(doc[i])))
if not all_pages:
print(f'[{series}] 无可用源 PDF')
continue
# 渲染顺序: 特性页 -> 尺寸页 -> 夹臂页(稳定顺序,便于对照)
order = {'char': 0, 'dim': 1, 'arm': 2, 'other': 3}
all_pages.sort(key=lambda x: order[x[2]])
n = render([(doc, i) for doc, i, k in all_pages], d, 1)
kinds = [k for _, _, k in all_pages]
print(f'[{series}] {len(all_pages)} 页 -> {kinds}')
# 数据表 PDF: 只保留该系列的页(合并多缸径 PDF)
out_pdf = fitz.open()
for name, doc in src_docs:
out_pdf.insert_pdf(doc)
out_path = os.path.join(d, series + '_manual.pdf')
out_pdf.save(out_path, garbage=3, deflate=True)
out_pdf.close()
print(f'[{series}] datasheet <- {len(pdfs)} 个PDF合并 ({os.path.getsize(out_path)/1024:.0f}KB)')
# UCBT: 从主目录提取
if UCBT_FROM_MAIN:
d = os.path.join(DST, 'UCBT')
if CLEAN and os.path.isdir(d):
shutil.rmtree(d)
os.makedirs(d, exist_ok=True)
main_pdf = os.path.join(SRC, MAIN_CATALOG)
doc = fitz.open(main_pdf)
# 找 UCBT 相关页(数据页 + 夹臂页)
# 注: 早期版本用 'UCBT40' 精确匹配 + 漏了 CLAMPING ARMS, 3 页只抓到 1 页。
# 主目录里 UCBT 占连续 3 页 (idx 40/41/42), 前后分别是 UCBM 与新章节, 故按 UCBT 匹配即准确。
idxs = []
for i in range(doc.page_count):
t = doc[i].get_text().upper()
if 'UCBT' in t and any(m in t for m in (
'DIMENSIONS', 'CODIFICATION', 'CHARACTERISTICS', 'CLAMPING ARMS')):
idxs.append(i)
if idxs:
n = render([(doc, i) for i in idxs], d, 1)
print(f'[UCBT] {len(idxs)} 页 <- 主目录 idx={idxs}')
# datasheet: 只裁 UCBT 那几页
out_pdf = fitz.open()
for i in idxs:
out_pdf.insert_pdf(doc, from_page=i, to_page=i)
out_path = os.path.join(d, 'UCBT_manual.pdf')
out_pdf.save(out_path, garbage=3, deflate=True)
out_pdf.close()
print(f'[UCBT] datasheet <- 裁剪 {len(idxs)} 页 ({os.path.getsize(out_path)/1024:.0f}KB)')
else:
print('[UCBT] 未找到数据页')
print('完成')
if __name__ == '__main__':
main()