数据 (CLM 气动夹紧缸分类): - 1 系列 demo -> 11 个官方系列, 6 型号 -> 859 个真实型号 - 参数化升级: 仅缸径 -> 最多 5 维 (缸径/臂位置/臂类型/臂样式/传感器) - 11 系列各含官方图册附图 (84 张) + datasheet PDF - 修复: 枚举维度空串必须为合法值 (否则级联过滤后下拉全空) - 修复: catalog.json 分类 series 数组未更新导致新系列不进包 - 修复: _step 副本后缀归一化错误 (产生假型号) 工具: - gen-clamp-series.py 数据清洗 (X盘数模 -> CSV/meta/STEP) - copy-clamp-step.py 8线程并行复制 (网络盘 I/O 优化) - gen-clamp-manual.py 官方图册 PDF -> 附图 - update-clamp-meta-attach.py / fix-clamp-meta-empty-enum.py 仓库瘦身: - .gitignore 排除数模(5GB)/.opc(1.2GB)/编译产物/发布产物 - 移出索引: release/ bin产物 STEP数模 .opc 大型PDF 等 - 提交体积 17MB (原 15.97GB) 其他: SelfTest.cs 可解析选型逻辑放宽 (稀疏数据用真实变体回验) Co-Authored-By: Claude Code <noreply@anthropic.com>
129 lines
4.9 KiB
Python
129 lines
4.9 KiB
Python
# -*- 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:
|
||
pix = doc[i].get_pixmap(dpi=DPI, colorspace=fitz.csGRAY)
|
||
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)
|
||
# 找 UCBT40 相关页(数据页)
|
||
idxs = []
|
||
for i in range(doc.page_count):
|
||
t = doc[i].get_text().upper()
|
||
if 'UCBT40' in t and any(m in t for m in ('DIMENSIONS', 'CODIFICATION', 'CHARACTERISTICS')):
|
||
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()
|