【办公类-109-09】20260228挂牌长方形挂牌卡片(只有word编辑单面,添加有效日期)(85*54mm横板和54*85mm竖版,一页9张)
背景需求制作幼儿挂牌里面的信息卡片这是上学期的样式,第一版 横版和竖版【办公类-109-04】20250807托小班新生的卡套长方形纸牌制作Python3.11.5 32(85*54mm横板和54*85mm竖版,一页9张)https://mp.csdn.net/mp_blog/creation/editor/150009366第二版,有照片位置预留,PDF【办公类-109-07】20250823(有1寸照片)托小班新生的卡套长方形纸牌制作Python3.11.5 32(85*54mm横板和54*85mm竖版,一页9https://mp.csdn.net/mp_blog/creation/editor/150774366第三班 word可编辑【办公类-109-05】20250915挂牌长方形卡片(word编辑单面)(85*54mm横板和54*85mm竖版,一页9张)https://mp.csdn.net/mp_blog/creation/editor/151723723这是上学期的样式,虽然我觉得好像放在卡套里的长方形竖版横版纸片用了6个月,基本没有坏。但是我还是再做一份下学期的备份。关键是添加上卡片的“有效日期”(2026.2-2026.6)代码部分''' 1.制作20250901托小班长方形卡片,横版85*54mm和竖版54*85mm,一页9张单面 2.添加姓名拼音显示功能(拼音在姓名上方,带声调) 3.python 3.7.8 3. 横版布局:单列显示园区和班级,双列显示拼音、姓名和学号 4. 竖版布局:单列显示所有信息 5. 添加照片占位符:2.5cm宽*3.5cm高,黑色框线,中间写"贴照片" 6、生成全部班级或生成指定班级(中1-中6班) 7、添加日期2026.2-2026.6 豆包,deepseek,阿夏 20260228 ''' # ========== 记录程序开始时间 ========== import time start_time = time.time() # ========== 创建照片占位符图片 ========== import os from copy import deepcopy from PIL import Image, ImageDraw, ImageFont import shutil import pandas as pd from pypinyin import pinyin, Style from docx import Document from docx.shared import Cm, Pt from docx.enum.text import WD_PARAGRAPH_ALIGNMENT from docx.oxml.ns import qn from docx.oxml import OxmlElement import glob from docx.oxml.shared import qn as oxml_qn from docx.oxml import parse_xml from docx.enum.text import WD_BREAK from docx.oxml.shared import qn from docx.oxml import parse_xml date='2026.2—2026.6' def create_photo_placeholder(image_path): """创建照片占位符图片""" try: # 确保目录存在 os.makedirs(os.path.dirname(image_path), exist_ok=True) # 设置图片尺寸 (2.5cm x 3.5cm) width, height = int(2.5/2.54*300), int(3.5/2.54*300) # 转换为300dpi的像素 # 创建白色背景图片 img = Image.new('RGB', (width, height), 'white') draw = ImageDraw.Draw(img) # 绘制黑色边框 draw.rectangle([(10, 10), (width-10, height-10)], outline='black', width=2) # 添加文字 try: # 尝试使用微软雅黑字体 font = ImageFont.truetype("msyh.ttc", 20) except: # 如果找不到字体,使用默认字体 font = ImageFont.load_default() text = "贴照片" text_bbox = draw.textbbox((0, 0), text, font=font) text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] text_x = (width - text_width) // 2 text_y = (height - text_height) // 2 draw.text((text_x, text_y), text, fill='black', font=font) # 保存图片 img.save(image_path) print(f"已创建照片占位符: {image_path}") return True except Exception as e: print(f"创建照片占位符失败: {str(e)}") return False # ========== 读取Excel数据 ========== def read_excel_data(file_path): """读取Excel文件所有工作表数据,返回字典{班级名: 数据列表}""" # 特殊姓氏读音字典 SPECIAL_SURNAMES = { "乐": "Yuè", "单": "Shàn", "解": "Xiè", "查": "Zhā", "盖": "Gě", "仇": "Qiú", "种": "Chóng", "朴": "Piáo", "翟": "Zhái", "区": "Ōu", "繁": "Pó", "覃": "Qín", "召": "Shào", "华": "Huà", "纪": "Jǐ", "曾": "Zēng", "缪": "Miào", "员": "Yùn", "车": "Chē", "过": "Guō", "尉": "Yù", "万": "Wàn" } # 复姓处理字典 COMPOUND_SURNAMES = { "欧阳": "Ōu yáng", "上官": "Shàng guān", "皇甫": "Huáng fǔ", "尉迟": "Yù chí", "万俟": "Mò qí", "长孙": "Zhǎng sūn", "司徒": "Sī tú", "司空": "Sī kōng", "司马": "Sī mǎ", "诸葛": "Zhū gě", "东方": "Dōng fāng", "独孤": "Dú gū", "慕容": "Mù róng", "宇文": "Yǔ wén" } def correct_surname_pinyin(name): """校正姓氏拼音,处理多音字问题""" if not name or not isinstance(name, str) or name.strip() == "": return "" name = name.strip() # 先检查复姓 for surname in COMPOUND_SURNAMES: if name.startswith(surname): surname_pinyin = COMPOUND_SURNAMES[surname] given_name = name[2:] given_pinyin = ' '.join([p[0] for p in pinyin(given_name, style=Style.TONE)]) return f"{surname_pinyin} {given_pinyin}" # 检查单姓 first_char = name[0] if first_char in SPECIAL_SURNAMES: surname_pinyin = SPECIAL_SURNAMES[first_char] given_name = name[1:] given_pinyin = ' '.join([p[0] for p in pinyin(given_name, style=Style.TONE)]) return f"{surname_pinyin} {given_pinyin}" return ' '.join([p[0] for p in pinyin(name, style=Style.TONE)]) try: xls = pd.ExcelFile(file_path) class_data = {} for sheet_name in xls.sheet_names: df = pd.read_excel( file_path, sheet_name=sheet_name, usecols=range(5), header=None, dtype={0: str} ) df = df.iloc[1:] data_list = [] for index, row in df.iterrows(): row_num = index + 2 raw_id = str(row[0]).strip() if pd.notna(row[0]) else "" # 如果没有学号但有名字,或者有学号但没有名字,都创建记录 name = str(row[3]).strip() if pd.notna(row[3]) else "" try: # 处理学号 if raw_id.isdigit(): id_num = int(raw_id) formatted_id = f"{id_num:02d}" id_display = f"{id_num}号" else: # 如果没有学号,使用行号作为学号 id_num = index + 1 formatted_id = f"{id_num:02d}" id_display = f"{id_num}号" campus = str(row[1]).strip() if pd.notna(row[1]) else "" class_name = str(row[2]).strip() if pd.notna(row[2]) else "" gender = str(row[4]).strip() if pd.notna(row[4]) else "" # 如果没有名字,使用"张三" if not name: name = "张三" name_pinyin = correct_surname_pinyin(name) data_list.append({ 'id_display': id_display, 'id_file': formatted_id, 'campus': campus, 'name': name, 'name_pinyin': name_pinyin, 'class_name': class_name, 'gender': gender }) except Exception as e: print(f"处理第{row_num}行数据时出错: {str(e)}") continue if data_list: class_data[sheet_name] = data_list return class_data except Exception as e: print(f"读取Excel文件失败:{str(e)}") return {} def expand_students_to_target_count(students, class_name): """将学生列表扩展到目标人数(托小班27人,中大班36人)""" # 确定目标人数 if "托" in class_name : target_count = 27 elif "小" in class_name: target_count = 27 elif "中" in class_name: target_count = 36 else: target_count = 36 # 如果实际人数已经达到或超过目标人数,直接返回实际人数 if len(students) = target_count: return students[:target_count] # 扩展学生列表 expanded_students = students.copy() # 获取第一个学生的园区和班级信息(用于填充空白学生) if students: base_campus = students[0]['campus'] base_class = students[0]['class_name'] else: base_campus = "" base_class = class_name # 添加空白学生 for i in range(len(students), target_count): empty_student = { 'id_display': f"{i+1}号",