_bid_prep_perf.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. # -*- coding: utf-8 -*-
  2. """把业绩证明材料渲染成可插入 Word 的 JPEG。
  3. 包含三类:
  4. 合同关键页 询比文件要求的首页、服务内容页、金额页、签字盖章页
  5. 结算发票 电子专票 PDF 单页;DOC2024-001 为 4 张纸质专票扫描件
  6. 查验截图 税务系统发票详情截图(现仅业绩3 的 4 张纸质专票有)
  7. 扫描件按 150dpi 出图后转 JPEG,兼顾可读性与文件体积。
  8. """
  9. import io
  10. import os
  11. import shutil
  12. import sys
  13. import zipfile
  14. import fitz
  15. from PIL import Image
  16. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
  17. HERE = os.path.dirname(os.path.abspath(__file__))
  18. MAT = (r"C:\Users\skygu\OneDrive\Projects\AIDOP\项目\项目招投标\相关准备材料")
  19. CON = os.path.join(MAT, "过往业绩合同")
  20. INV = os.path.join(CON, "发票")
  21. OUT = os.path.join(HERE, "_bid_assets", "perf")
  22. DPI, Q = 150, 85
  23. # 业绩序号 → (合同文件, 关键页页码[首页/服务内容/金额/签字盖章])
  24. CONTRACTS = [
  25. (1, "DOC2025003.pdf", [1, 2, 3, 5]),
  26. (2, "DOC2024004.pdf", [1, 2, 3, 8]),
  27. (3, "DOC2024-001.pdf", [1, 2, 3, 8]),
  28. (4, "DOC2024003.pdf", [1, 2, 3, 8]),
  29. (5, "DOC2025002.pdf", [1, 2, 3, 8]),
  30. ]
  31. # 业绩序号 → 电子发票文件名(业绩3 为纸质专票,另行处理)
  32. EINV = {
  33. 1: "dzfp_26112000002914765426_河南产业互联网联合发展有限公司_20260804170557.pdf",
  34. 2: "dzfp_24112000000220367465_智盛易(天津)科技有限公司_20260804170417.pdf",
  35. 4: "dzfp_24112000000142493545_智盛易(天津)科技有限公司_20260804170418.pdf",
  36. 5: "dzfp_25112000000135255646_智盛易(天津)科技有限公司_20260804170514.pdf",
  37. }
  38. MAXW = 1700 # 插入宽度约 14cm,再高的像素只会白占体积
  39. def save(pix_or_im, name):
  40. im = pix_or_im
  41. if not isinstance(im, Image.Image):
  42. im = Image.frombytes("RGB", (im.width, im.height), im.samples)
  43. if im.mode != "RGB":
  44. im = im.convert("RGB")
  45. if im.width > MAXW:
  46. im = im.resize((MAXW, round(im.height * MAXW / im.width)), Image.LANCZOS)
  47. p = os.path.join(OUT, name)
  48. im.save(p, "JPEG", quality=Q, subsampling=0, optimize=True)
  49. print(f" {name:<34} {im.width}×{im.height} {os.path.getsize(p) / 1024:6.0f}KB")
  50. return p
  51. def pdf_page(path, page, name):
  52. d = fitz.open(path)
  53. pix = d[page - 1].get_pixmap(dpi=DPI)
  54. p = save(pix, name)
  55. d.close()
  56. return p
  57. def main():
  58. if os.path.exists(OUT):
  59. shutil.rmtree(OUT)
  60. os.makedirs(OUT)
  61. total = 0
  62. print("合同关键页")
  63. tags = ["首页", "服务内容页", "金额页", "签字盖章页"]
  64. for no, fn, pages in CONTRACTS:
  65. for tag, pg in zip(tags, pages):
  66. pdf_page(os.path.join(CON, fn), pg, f"c{no}_{tag}.jpg")
  67. total += 1
  68. print("\n电子发票")
  69. for no, fn in sorted(EINV.items()):
  70. src = os.path.join(INV, fn)
  71. if not os.path.exists(src): # 24/25 年两批是压缩包内的
  72. src = find_in_zip(fn)
  73. pdf_page(src, 1, f"i{no}_发票.jpg")
  74. total += 1
  75. print("\n业绩3 纸质专票(4 张)")
  76. d = fitz.open(os.path.join(INV, "北京增值税专用发票.pdf"))
  77. for i in range(d.page_count):
  78. save(d[i].get_pixmap(dpi=DPI), f"i3_发票{i + 1}.jpg")
  79. total += 1
  80. d.close()
  81. print("\n税务系统查验截图(业绩3 的 4 张专票)")
  82. pngs = sorted(n for n in os.listdir(INV) if n.endswith(".png"))
  83. for i, n in enumerate(pngs, 1):
  84. save(Image.open(os.path.join(INV, n)), f"v3_查验{i}.jpg")
  85. total += 1
  86. print("\n劳动合同(8 页)")
  87. d = fitz.open(os.path.join(MAT, "DOC劳动合同.pdf"))
  88. for i in range(d.page_count):
  89. save(d[i].get_pixmap(dpi=DPI), f"lab_p{i + 1}.jpg")
  90. total += 1
  91. d.close()
  92. mb = sum(os.path.getsize(os.path.join(OUT, f))
  93. for f in os.listdir(OUT)) / 1024 / 1024
  94. print(f"\n共 {total} 张,合计 {mb:.1f}MB → {OUT}")
  95. def find_in_zip(fn):
  96. """两批电子发票在 zip 里,解到临时目录后返回路径。"""
  97. tmp = os.path.join(HERE, "_tmp_inv")
  98. for root, _, files in os.walk(tmp):
  99. if fn in files:
  100. return os.path.join(root, fn)
  101. for z in (n for n in os.listdir(INV) if n.endswith(".zip")):
  102. with zipfile.ZipFile(os.path.join(INV, z)) as zf:
  103. for info in zf.infolist():
  104. try:
  105. nm = info.filename.encode("cp437").decode("gbk")
  106. except Exception:
  107. nm = info.filename
  108. if nm == fn:
  109. os.makedirs(tmp, exist_ok=True)
  110. dst = os.path.join(tmp, fn)
  111. with zf.open(info) as s, open(dst, "wb") as t:
  112. t.write(s.read())
  113. return dst
  114. raise FileNotFoundError(fn)
  115. if __name__ == "__main__":
  116. main()