Excel嵌入圖片至單元格
from PIL import Image
import os
import zipfile
import tempfile
import xml.etree.ElementTree as ET
import secrets
class ExcelHandler:
? ?def __init__(self):
? ? ? ?self.map_img = {}
? ? ? ?self.xml_declaration = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
? ? ? ?self.cell_images_rels_path = "xl/_rels/cellimages.xml.rels"
? ? ? ?self.cell_images_path = "xl/cellimages.xml"
? ? ? ?self.sheet_path = "xl/worksheets/{}.xml"
? ?def add_img(self, zip_file, img_paths):
? ? ? ?"""添加圖片"""
? ? ? ?def generate_random_filename():
? ? ? ? ? ?filename = secrets.token_bytes(16).hex().upper() + ".png"
? ? ? ? ? ?return filename
? ? ? ?def get_img_size(path):
? ? ? ? ? ?# 打開圖像并獲取大小
? ? ? ? ? ?with Image.open(path) as im:
? ? ? ? ? ? ? ?width, height = im.size
? ? ? ? ? ?return width, height
? ? ? ?for img_path in img_paths:
? ? ? ? ? ?img_name = os.path.basename(img_path)
? ? ? ? ? ?new_img_name = generate_random_filename()
? ? ? ? ? ?zip_file.write(img_path, arcname=f"xl/media/{new_img_name}")
? ? ? ? ? ?size = get_img_size(img_path)
? ? ? ? ? ?self.map_img[img_name] = {"path": f"media/{new_img_name}", "size": size}
? ?@staticmethod
? ?def create_id(ids, pre=""):
? ? ? ?new_id = pre + "1"
? ? ? ?num = 1
? ? ? ?while new_id in ids:
? ? ? ? ? ?num += 1
? ? ? ? ? ?new_id = f"{pre}{num}"
? ? ? ?return new_id
? ?def write_cell_images_rels(self, z_in, z_out):
? ? ? ?if self.cell_images_rels_path not in z_in.namelist():
? ? ? ? ? ?raise Exception("請(qǐng)先在表格中任意嵌入一張圖片")
? ? ? ?else:
? ? ? ? ? ?root = ET.fromstring(z_in.read(self.cell_images_rels_path))
? ? ? ?relationships = root.findall(
? ? ? ? ? ?'{http://schemas.openxmlformats.org/package/2006/relationships}Relationship')
? ? ? ?rids = list(map(lambda node: node.get('Id'), relationships))
? ? ? ?rid = self.create_id(rids, pre="rId")
? ? ? ?rids.append(rid)
? ? ? ?for img_name, info in self.map_img.items():
? ? ? ? ? ?new_element = ET.Element('ns0:Relationship')
? ? ? ? ? ?new_element.set('Id', rid)
? ? ? ? ? ?new_element.set('Type', "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
? ? ? ? ? ?new_element.set('Target', info["path"])
? ? ? ? ? ?root.insert(-1, new_element)
? ? ? ? ? ?info["rid"] = rid
? ? ? ? ? ?self.map_img[img_name] = info
? ? ? ?xml_str = ET.tostring(root)
? ? ? ?z_out.writestr(self.cell_images_rels_path, self.xml_declaration + xml_str.decode())
? ?def write_cell_images(self, z_in, z_out):
? ? ? ?def create_cell_image(_id, img_uuid, descr, rid, cx, cy):
? ? ? ? ? ?cell_image = ET.SubElement(root, 'ns0:cellImage')
? ? ? ? ? ?pic = ET.SubElement(cell_image, 'ns1:pic')
? ? ? ? ? ?nvPicPr = ET.SubElement(pic, 'ns1:nvPicPr')
? ? ? ? ? ?cNvPr = ET.SubElement(nvPicPr, 'ns1:cNvPr', attrib={
? ? ? ? ? ? ? ?'id': _id,
? ? ? ? ? ? ? ?'name': img_uuid,
? ? ? ? ? ? ? ?'descr': descr
? ? ? ? ? ?})
? ? ? ? ? ?cNvPicPr = ET.SubElement(nvPicPr, 'ns1:cNvPicPr')
? ? ? ? ? ?picLocks = ET.SubElement(cNvPicPr, 'ns2:picLocks', attrib={
? ? ? ? ? ? ? ?'noChangeAspect': '1'
? ? ? ? ? ?})
? ? ? ? ? ?blipFill = ET.SubElement(pic, 'ns1:blipFill')
? ? ? ? ? ?blip = ET.SubElement(blipFill, 'ns2:blip', attrib={
? ? ? ? ? ? ? ?'ns3:embed': rid
? ? ? ? ? ?})
? ? ? ? ? ?stretch = ET.SubElement(blipFill, 'ns2:stretch')
? ? ? ? ? ?fillRect = ET.SubElement(stretch, 'ns2:fillRect')
? ? ? ? ? ?spPr = ET.SubElement(pic, 'ns1:spPr')
? ? ? ? ? ?xfrm = ET.SubElement(spPr, 'ns2:xfrm')
? ? ? ? ? ?off = ET.SubElement(xfrm, 'ns2:off', attrib={
? ? ? ? ? ? ? ?'x': '66',
? ? ? ? ? ? ? ?'y': '66'
? ? ? ? ? ?})
? ? ? ? ? ?ext = ET.SubElement(xfrm, 'ns2:ext', attrib={
? ? ? ? ? ? ? ?'cx': cx,
? ? ? ? ? ? ? ?'cy': cy
? ? ? ? ? ?})
? ? ? ? ? ?prstGeom = ET.SubElement(spPr, 'ns2:prstGeom', attrib={
? ? ? ? ? ? ? ?'prst': 'rect'
? ? ? ? ? ?})
? ? ? ? ? ?avLst = ET.SubElement(prstGeom, 'ns2:avLst')
? ? ? ?if self.cell_images_rels_path not in z_in.namelist():
? ? ? ? ? ?raise Exception("請(qǐng)先在表格中任意嵌入一張圖片")
? ? ? ?else:
? ? ? ? ? ?root = ET.fromstring(z_in.read(self.cell_images_path))
? ? ? ?namespaces = {'xdr': 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing'}
? ? ? ?ids = [c_nv_pr.get('id') for c_nv_pr in root.findall('.//xdr:cNvPr', namespaces)]
? ? ? ?for img_name, info in self.map_img.items():
? ? ? ? ? ?w, h = info["size"]
? ? ? ? ? ?uuid = f'ID_{secrets.token_bytes(16).hex().upper()}'
? ? ? ? ? ?create_cell_image(self.create_id(ids), uuid, img_name, info["rid"], str(w * 9525), str(h * 9525))
? ? ? ? ? ?info["img_uuid"] = uuid
? ? ? ? ? ?self.map_img[img_name] = info
? ? ? ?xml_str = ET.tostring(root)
? ? ? ?z_out.writestr(self.cell_images_path, self.xml_declaration + xml_str.decode())
? ?def write_sheet(self, z_in, z_out, sheet_path, row, col, img_uuid):
? ? ? ?"""
? ? ? ?插入圖片
? ? ? ?"""
? ? ? ?root = ET.fromstring(z_in.read(sheet_path))
? ? ? ?pre = ""
? ? ? ?row_node = ET.Element(f'{pre}row')
? ? ? ?row_node.set('r', row)
? ? ? ?row_node.set('spans', '1:1')
? ? ? ?row_node.set('customHeight', '1')
? ? ? ?cell_node = ET.SubElement(row_node, f"{pre}c")
? ? ? ?cell_node.set("r", f"{col.upper()}{row}")
? ? ? ?cell_node.set("t", "str")
? ? ? ?f_node = ET.SubElement(cell_node, f"{pre}f")
? ? ? ?v_node = ET.SubElement(cell_node, f"{pre}v")
? ? ? ?f_node.text = f'_xlfn.DISPIMG("{img_uuid}",1)'
? ? ? ?v_node.text = f'=DISPIMG("{img_uuid}",1)'
? ? ? ?sheet_data = root.find('{http://schemas.openxmlformats.org/spreadsheetml/2006/main}sheetData')
? ? ? ?sheet_data.append(row_node)
? ? ? ?xml_str = ET.tostring(root)
? ? ? ?z_out.writestr(sheet_path, self.xml_declaration + xml_str.decode())
? ?def embed_img_to_cell(self, excel_path, sheet_name, img_path, row, col):
? ? ? ?"""
? ? ? ?嵌入圖片至單元格
? ? ? ?:param excel_path: 表格路徑
? ? ? ?:param sheet_name: 工作表名稱
? ? ? ?:param img_path: 圖片路徑
? ? ? ?:param row: 行
? ? ? ?:param col: 列
? ? ? ?:return:
? ? ? ?"""
? ? ? ?temp_path = ""
? ? ? ?sheet_name = sheet_name.lower()
? ? ? ?sheet_path = self.sheet_path.format(sheet_name)
? ? ? ?row = str(row)
? ? ? ?col = str(col).upper()
? ? ? ?try:
? ? ? ? ? ?temp_fd, temp_path = tempfile.mkstemp(dir=os.path.dirname(excel_path))
? ? ? ? ? ?os.close(temp_fd)
? ? ? ? ? ?with zipfile.ZipFile(excel_path, 'r') as z_in, zipfile.ZipFile(temp_path, 'a') as z_out:
? ? ? ? ? ? ? ?if sheet_path not in z_in.namelist():
? ? ? ? ? ? ? ? ? ?raise Exception(f"工作表{sheet_name}不存在")
? ? ? ? ? ? ? ?z_out.comment = z_in.comment
? ? ? ? ? ? ? ?self.add_img(z_out, [img_path])
? ? ? ? ? ? ? ?self.write_cell_images_rels(z_in, z_out)
? ? ? ? ? ? ? ?self.write_cell_images(z_in, z_out)
? ? ? ? ? ? ? ?self.write_sheet(z_in, z_out, sheet_path, row, col, self.map_img[img_path]["img_uuid"])
? ? ? ? ? ? ? ?for item in z_in.infolist():
? ? ? ? ? ? ? ? ? ?filename = item.filename
? ? ? ? ? ? ? ? ? ?if filename == self.cell_images_rels_path:
? ? ? ? ? ? ? ? ? ? ? ?continue
? ? ? ? ? ? ? ? ? ?if filename == self.cell_images_path:
? ? ? ? ? ? ? ? ? ? ? ?continue
? ? ? ? ? ? ? ? ? ?if filename == sheet_path:
? ? ? ? ? ? ? ? ? ? ? ?continue
? ? ? ? ? ? ? ? ? ?z_out.writestr(item, z_in.read(filename))
? ? ? ? ? ?os.remove(excel_path)
? ? ? ? ? ?os.rename(temp_path, excel_path)
? ? ? ?finally:
? ? ? ? ? ?if os.path.exists(temp_path):
? ? ? ? ? ? ? ?os.remove(temp_path)
if __name__ == '__main__':
? ?execl_handler = ExcelHandler()
? ?execl_handler.embed_img_to_cell("新建 XLSX 工作表.xlsx", "sheet2", "150x150.png", 7, 2)