基于Python的ReportLab实现4×5图片网格生成方案问询
用ReportLab Table实现4×5照片接触印样的最佳方案
嘿,我完全懂你想用ReportLab做照片接触印样的需求——手动用drawImage一个个加确实太繁琐了,用Table组件来规整布局绝对是更聪明的选择!我帮你梳理了一套可行的实现方案,直接上代码和关键说明:
核心思路
每个Table单元格需要包含图片(保持宽高比)和下方的文件名,所以我们要给每个单元格生成一个包含图片元素和文本元素的列表,让Table自动垂直排列它们。同时要计算好单元格的尺寸,适配A4纸的4列×5行布局,还要处理图片的缩放逻辑避免变形。
完整实现代码
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Image, Paragraph from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import ParagraphStyle from PIL import Image as PILImage import os def rowGen(lst, n): """按n个元素一组生成行数据""" for i in range(0, len(lst), n): yield lst[i:i + n] def create_image_cell(image_path, cell_width, cell_height): """生成包含图片和文件名的单元格内容""" # 用PIL获取图片原始宽高,计算缩放比例 with PILImage.open(image_path) as img: orig_w, orig_h = img.size # 预留20点高度给文件名,计算图片的最大可用高度 max_img_height = cell_height - 20 scale = max_img_height / orig_h img_width = orig_w * scale # 如果缩放后图片宽度超过单元格宽度,再按宽度缩放 if img_width > cell_width: scale = cell_width / orig_w img_height = orig_h * scale img_width = cell_width else: img_height = max_img_height # 创建ReportLab图片对象 rl_image = Image(image_path, width=img_width, height=img_height) # 创建居中显示的文件名文本(用Paragraph美化样式) filename_style = ParagraphStyle( name="FileNameStyle", alignment=1, # 1表示居中 fontSize=10, fontName="Helvetica" ) filename_paragraph = Paragraph(os.path.basename(image_path), filename_style) # 返回图片+文本的列表,Table会自动垂直排列这两个元素 return [rl_image, filename_paragraph] def makePDF(document_title, data): """生成PDF文档""" # 初始化文档,设置边距 pdf = SimpleDocTemplate( document_title, pagesize=A4, leftMargin=20, rightMargin=20, topMargin=20, bottomMargin=20 ) # 定义Table样式:居中对齐、垂直居中、设置内边距 table_style = TableStyle([ ('ALIGN', (0, 0), (-1, -1), 'CENTER'), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('BOTTOMPADDING', (0, 0), (-1, -1), 5), # 可选:添加网格线方便调试,发布时可以注释掉 # ('BOX', (0,0), (-1,-1), 1, (0,0,0)), # ('INNERGRID', (0,0), (-1,-1), 0.5, (128,128,128)) ]) # 定义列宽和行高:A4纸扣除边距后,4列每列约138点,5行每行约160点 col_widths = [138] * 4 row_heights = [160] * 5 # 创建Table并应用样式 table = Table(data, colWidths=col_widths, rowHeights=row_heights) table.setStyle(table_style) # 构建PDF pdf.build([table]) if __name__ == "__main__": # 图片目录路径 img_dir = "Images/" # 获取目录下所有jpg图片的完整路径 image_paths = [ os.path.join(img_dir, filename) for filename in os.listdir(img_dir) if filename.lower().endswith(".jpg") ] # 生成每个图片对应的单元格内容,再按4个一组分成行 cell_contents = [create_image_cell(path, 138, 160) for path in image_paths] table_data = list(rowGen(cell_contents, 4)) # 处理最后一行不足4个的情况,补空单元格避免布局错乱 if len(table_data[-1]) < 4: table_data[-1] += [[]] * (4 - len(table_data[-1])) # 生成PDF makePDF("Photo_Contact_Sheet.pdf", table_data)
关键细节说明
- 图片缩放逻辑:用PIL获取原始图片尺寸,先按单元格高度(扣除文件名高度)缩放,若宽度超过单元格则再按宽度缩放,确保图片始终保持原始宽高比且完全适配单元格。
- 单元格内容结构:每个单元格用
[图片元素, 文本段落]的列表,Table会自动将这两个元素垂直居中排列,完美实现图片在上、文件名在下的效果。 - Table尺寸控制:手动设置
colWidths和rowHeights,保证每个单元格大小统一,适配A4纸的4×5布局。 - 空单元格填充:如果图片总数不是4的倍数,给最后一行补空单元格,避免Table布局出现错乱。
内容的提问来源于stack exchange,提问作者Petchalxande




