惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
C
CERT Recently Published Vulnerability Notes
S
Security @ Cisco Blogs
Google DeepMind News
Google DeepMind News
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Tenable Blog
O
OpenAI News
NISL@THU
NISL@THU
AI
AI
T
Threat Research - Cisco Blogs
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
Schneier on Security
Schneier on Security
W
WeLiveSecurity
P
Privacy International News Feed
Cisco Talos Blog
Cisco Talos Blog
S
Secure Thoughts
D
Docker
博客园 - 三生石上(FineUI控件)
T
Troy Hunt's Blog
T
Threatpost
C
Cyber Attacks, Cyber Crime and Cyber Security
Vercel News
Vercel News
H
Hacker News: Front Page
B
Blog
S
SegmentFault 最新的问题
H
Heimdal Security Blog
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
G
GRAHAM CLULEY
AWS News Blog
AWS News Blog
N
News and Events Feed by Topic
L
LINUX DO - 热门话题
小众软件
小众软件
爱范儿
爱范儿
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN
Recent Announcements
Recent Announcements
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
PCI Perspectives
PCI Perspectives
I
Intezer
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed

博客园 - 与f

微软的人机验证 docker运行hermes agent Python 项目创建+依赖管理+版本控制 百度飞桨PaddleOCR-VL识别发票图片输出json格式 大模型微调后导出,部署服务然后后端调用 openclaw的一些聊天配置 docker 部署 openclaw llama.cpp编译 微信小程序显示ai返回的markdown或html C#多端桌面程序(可跨平台) 主流 MySQL 热备份方案 基于SRS(Simple Realtime Server)+ CDN边缘节点加速3个nginx做文件分发1个nginx做负载均衡 一份基于SRS(Simple Realtime Server)+ CDN边缘节点srs edge加速的万人级直播部署清单 HLS(HTTP Live Streaming)标准的视频及加密和解密播放 HLS(HTTP Live Streaming)标准的视频及加密 使用Dockerfile创建一个hyperf容器做为开发环境 docker的hyperf框架docker-compose一键安装 通过vm虚拟中的docker 环境做开发 ubuntu 安装后ssh 连接到服务器 lvm的概念和操作(linux) PHP Attributes 注解 centos 服务器查找一些字符串 Centos8网络配置小工具 Keepalive实现一个高可用负载均衡场景 Keepalived详解:原理、编译安装与高可用集群配置
python使用docx库对在word中的表格合并操作
与f · 2026-04-25 · via 博客园 - 与f
from docx import Document

def merge_cells_horizontally(table, row: int, start_col: int, end_col: int) -> None:
    """水平合并单元格
    :param table: 表格对象
    :param row: 行索引(从0开始)
    :param start_col: 起始列索引
    :param end_col: 结束列索引
    """
    if start_col >= end_col:
        raise ValueError("结束列必须大于起始列")
    cell = table.cell(row, start_col)
    for col in range(start_col + 1, end_col + 1):
        cell.merge(table.cell(row, col))
def merge_cells_vertically(table, col: int, start_row: int, end_row: int) -> None:
    """垂直合并单元格
    :param table: 表格对象
    :param col: 列索引(从0开始)
    :param start_row: 起始行索引
    :param end_row: 结束行索引
    """
    if start_row >= end_row:
        raise ValueError("结束行必须大于起始行")
    cell = table.cell(start_row, col)
    for row in range(start_row + 1, end_row + 1):
        cell.merge(table.cell(row, col))
def create_sample_document(output_path: str) -> None:
    doc = Document()
    doc.add_heading('表格单元格合并演示', level=1)
    
    # 创建5x5表格
    table = doc.add_table(rows=5, cols=5)
    
    # 填充基础数据
    for row_idx in range(5):
        for col_idx in range(5):
            table.cell(row_idx, col_idx).text = f"({row_idx},{col_idx})"
    
    # 水平合并示例
    merge_cells_horizontally(table, 0, 1, 3)
    table.cell(0, 1).text = "水平合并单元格"
    
    # 垂直合并示例
    merge_cells_vertically(table, 4, 1, 3)
    table.cell(1, 4).text = "垂直合并单元格"
    
    # 复杂合并示例
    for row in range(2, 5):
        merge_cells_horizontally(table, row, 0, 2)
    merge_cells_vertically(table, 0, 2, 4)
    table.cell(2, 0).text = "复杂合并"
    
    doc.save(output_path)
import os
from docx import Document
from docx.shared import Cm, Pt ,Inches
from docx.oxml.shared import OxmlElement, qn
from docx.enum.table import WD_ROW_HEIGHT_RULE
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_ALIGN_VERTICAL


def set_page_format(doc):
    """设置A4纸张,并调整页边距为窄边距,以容纳3个表格"""
    section = doc.sections[0]
    section.page_width = Cm(21.0)          # A4宽度
    section.page_height = Cm(29.7)         # A4高度
    section.left_margin = Cm(1.2)
    section.right_margin = Cm(1.2)
    section.top_margin = Cm(1.2)
    section.bottom_margin = Cm(1.2)

def set_cell_font(cell, text, bold=False, size_pt=12):
    """设置单元格文本及字体(宋体,默认12号)"""
    cell.text = text
    paragraph = cell.paragraphs[0]
    run = paragraph.runs[0] if paragraph.runs else paragraph.add_run(text)
    run.font.name = '宋体'
    run.font.size = Pt(size_pt)
    run.font.bold = bold
    # 垂直居中
    cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
    paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT

def add_reimbursement_table(doc, data):
    """添加一个完整的费用报销单(标题+表格)"""
    # 1. 添加标题“费用报销单”并居中加粗
    title_para = doc.add_paragraph()
    title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = title_para.add_run("\n费用报销单")
    run.font.size = Pt(14)
    run.font.bold = True
    run.font.name = '宋体'

    # 2. 创建表格:6行4列
    table = doc.add_table(rows=6, cols=6)
    table.style = 'Table Grid'          # 带边框的表格样式
    # 可选:统一设置每行最小高度,避免过于拥挤
    for row in table.rows:
        row.height = Cm(0.7)

    # 获取所有单元格的引用 (行索引0~5)
    merge_cells_horizontally(table, 0, 2, 5) # 表格第1行的第3列到第6列合并
    # 第1行:附单据 | 张数
    set_cell_font(table.cell(0, 0), "附单据", bold=False)
    set_cell_font(table.cell(0, 1), f"{data['attach_num']}张")
    table.rows[0].height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST # 行高设置
    table.rows[0].height = Pt(20) # 行高设置


    # 第2行:公司 | {公司名} | 日期 | {日期}
    merge_cells_horizontally(table, 1, 1, 3) # 表格第2行的第2列到第4列合并

    set_cell_font(table.cell(1, 0), "公司", bold=False)
    set_cell_font(table.cell(1, 1), data['company'])
    set_cell_font(table.cell(1, 4), "日期", bold=False)
    set_cell_font(table.cell(1, 5), data['date'])
    table.rows[1].height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST # 行高设置
    table.rows[1].height = Pt(26) # 行高设置

    # 第3行:申请金额(大写) | 大写金额 | 小写金额 | 小写金额值
    merge_cells_horizontally(table, 2, 1, 3) # 表格第2行的第2列到第4列合并
    set_cell_font(table.cell(2, 0), "申请金额\n(大写)", bold=False)
    set_cell_font(table.cell(2, 1), data['amount_cn'])
    set_cell_font(table.cell(2, 4), "小写(¥)", bold=False)
    set_cell_font(table.cell(2, 5), data['amount_num'])
    table.rows[2].height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST # 行高设置
    table.rows[2].height = Pt(40) # 行高设置


    # 第4行:用途说明 + 合并后四列
    set_cell_font(table.cell(3, 0), "经办人", bold=False)
    set_cell_font(table.cell(3, 1), data['handler'])
    # 合并第4行的第3,4,5,6列
    table.cell(3, 2).merge(table.cell(3, 3)).merge(table.cell(3, 4)).merge(table.cell(3, 5))
    table.rows[3].height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST # 行高设置
    table.rows[3].height = Pt(26) # 行高设置

    # 第5行:主管 | (空白) | 合并后四列
    set_cell_font(table.cell(4, 0), "主管", bold=False)
    set_cell_font(table.cell(4, 1), "")
    # 合并第4行的第3,4,5,6列
    table.cell(4, 2).merge(table.cell(4, 3)).merge(table.cell(4, 4)).merge(table.cell(4, 5))
    table.rows[4].height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST # 行高设置
    table.rows[4].height = Pt(26) # 行高设置

    # 第四行 第五行,后四列在行合并 | 添加报销用途
    merge_cells_vertically(table,2,3,4)
    set_cell_font(table.cell(4, 2), "用途说明:\n {}".format(data['purpose'] ), bold=False)


    # 第6行:出纳 | (空白) |出纳 | (空白) | 领款人 | (空白)
    set_cell_font(table.cell(5, 0), "会计", bold=False)
    set_cell_font(table.cell(5, 1), "")      # 留白
    set_cell_font(table.cell(5, 2), "出纳", bold=False)
    set_cell_font(table.cell(5, 3), "")
    set_cell_font(table.cell(5, 4), "领款人", bold=False)
    set_cell_font(table.cell(5, 5), "")
    table.rows[5].height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST # 行高设置
    table.rows[5].height = Pt(26) # 行高设置

    # 表格后添加一个空行(隔开下一个报销单),不要太多,避免溢出页面
    doc.add_paragraph("")

    # 3. 设置第一行的左、上、右边框为灰色
    set_first_row_top_borders(
        table,
        color="#808080",  # 灰色
        size=6,  # 0.7磅
        style="dashed"  # 实线single
    )


# 水平合并单元格
def merge_cells_horizontally(table, row: int, start_col: int, end_col: int) -> None:
    """水平合并单元格
    :param table: 表格对象
    :param row: 行索引(从0开始)
    :param start_col: 起始列索引
    :param end_col: 结束列索引
    """
    if start_col >= end_col:
        raise ValueError("结束列必须大于起始列")
    cell = table.cell(row, start_col)
    for col in range(start_col + 1, end_col + 1):
        cell.merge(table.cell(row, col))

# 垂直合并单元格
def merge_cells_vertically(table, col: int, start_row: int, end_row: int) -> None:
    """垂直合并单元格
    :param table: 表格对象
    :param col: 列索引(从0开始)
    :param start_row: 起始行索引
    :param end_row: 结束行索引
    """
    if start_row >= end_row:
        raise ValueError("结束行必须大于起始行")
    cell = table.cell(start_row, col)
    for row in range(start_row + 1, end_row + 1):
        cell.merge(table.cell(row, col))

# 设置单个单元格的边框颜色
def set_cell_border(cell, **kwargs):
    """
    设置单元格边框颜色和样式

    参数:
        cell: 单元格对象
        **kwargs: 边框属性,如:
            top={"sz": 12, "val": "single", "color": "#FF0000"}
            left={"sz": 12, "val": "single", "color": "#00FF00"}
            bottom={"sz": 12, "val": "single", "color": "#0000FF"}
            right={"sz": 12, "val": "single", "color": "#000000"}
            insideH={"sz": 12, "val": "single", "color": "#CCCCCC"}  # 水平内边框
            insideV={"sz": 12, "val": "single", "color": "#CCCCCC"}  # 垂直内边框

    尺寸单位: 1磅 = 8个单位 (sz=12 表示 1.5磅)
    颜色格式: 十六进制 "#RRGGBB" 或 "auto"
    """
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()

    # 创建边框元素
    tcBorders = OxmlElement('w:tcBorders')

    # 为每个边框方向设置属性
    for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
        edge_data = kwargs.get(edge)
        if edge_data:
            edge_element = OxmlElement(f'w:{edge}')

            # 设置尺寸
            if 'sz' in edge_data:
                edge_element.set(qn('w:sz'), str(edge_data['sz']))

            # 设置线型 (single, double, dotted, dashed, etc.)
            if 'val' in edge_data:
                edge_element.set(qn('w:val'), edge_data['val'])

            # 设置颜色
            if 'color' in edge_data:
                edge_element.set(qn('w:color'), edge_data['color'])

            # 设置阴影 (可选)
            if 'shadow' in edge_data:
                edge_element.set(qn('w:shadow'), edge_data['shadow'])

            # 设置空格 (可选)
            if 'space' in edge_data:
                edge_element.set(qn('w:space'), str(edge_data['space']))

            tcBorders.append(edge_element)

    tcPr.append(tcBorders)

# 设置整个表格的边框颜色
def set_table_border(table, **kwargs):
    """
    设置整个表格所有单元格的边框

    参数:
        table: 表格对象
        **kwargs: 同 set_cell_border

    示例:
        set_table_border(table,
                        top={"sz": 12, "val": "single", "color": "#FF0000"},
                        left={"sz": 12, "val": "single", "color": "#FF0000"},
                        bottom={"sz": 12, "val": "single", "color": "#FF0000"},
                        right={"sz": 12, "val": "single", "color": "#FF0000"},
                        insideH={"sz": 8, "val": "single", "color": "#0000FF"},
                        insideV={"sz": 8, "val": "single", "color": "#0000FF"})
    """
    for row in table.rows:
        for cell in row.cells:
            set_cell_border(cell, **kwargs)

# 设置表格第一行所有单元格的左、上、右边框为指定颜色
def set_first_row_top_borders(table, color="#808080", size=12, style="single"):
    """
    设置表格第一行所有单元格的左、上、右边框为指定颜色

    参数:
        table: 表格对象
        color: 边框颜色(十六进制),默认灰色 "#808080"
        size: 边框粗细(1/8磅),默认12(1.5磅)
        style: 边框样式,如 "single", "dotted", "dashed" 等
    """
    # 获取第一行
    first_row = table.rows[0]

    # 遍历第一行的所有单元格
    for cell in first_row.cells:
        set_cell_border(
            cell,
            top={"sz": size, "val": style, "color": color},  # 顶部边框
            left={"sz": size, "val": style, "color": color},  # 左侧边框
            right={"sz": size, "val": style, "color": color}  # 右侧边框
            # 注意:没有设置 bottom,所以底部边框保持默认
        )


def main():
    # 创建新文档
    doc = Document()
    set_page_format(doc)

    # ------------------------------------------------------------
    # 配置数据:生成3个报销单,每个报销单的变量值不同
    # ------------------------------------------------------------
    reimbursements = [
        {
            "attach_num": "8",  # 附单据张数
            "company": "西安服务公司",
            "date": "2026.4.23",
            "amount_cn": "叁佰柒拾贰元贰角贰分",
            "amount_num": "372.22",
            "handler": "李某某",
            "purpose": "交通"
        },
        {
            "attach_num": "2",
            "company": "西安服务有限公司",
            "date": "2026.4.23",
            "amount_cn": "壹仟叁佰伍拾玖元叁角捌分",
            "amount_num": "1359.38",
            "handler": "李某某",
            "purpose": "餐饮"
        },
        {
            "attach_num": "3",
            "company": "汽车服务有限公司",
            "date": "2026.4.23",
            "amount_cn": "肆佰伍拾陆元整",
            "amount_num": "456.00",
            "handler": "王芳",
            "purpose": "办公用品"
        }
    ]

    # 依次添加3个报销单表格(根据数据列表)
    for idx, data in enumerate(reimbursements):
        add_reimbursement_table(doc, data)

    # 保存生成的Word文件
    output_path = "费用报销单_A4_三张.docx"
    doc.save(output_path)
    print(f"生成成功!文件已保存为:{output_path}")
    print("可直接打开该文件使用Word打印(A4纸张,三个表格竖向排列)。")

if __name__ == "__main__":
    main()