# -*- coding: utf-8 -*- """ 为夹紧缸 11 个系列生成附图(多页尺寸图 + 裁剪版数据表 PDF)。 来源:Univer 官方图册 PDF(X 盘)。 用法: PYTHONUTF8=1 python gen-clamp-manual.py [--clean] --clean 先清空各系列 manual 目录再生成(避免旧页残留) 输出: manual/<系列>/p.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.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()