first commit

main
还不如一只猪威武 2 days ago
commit fb4514e91d

4
.gitignore vendored

@ -0,0 +1,4 @@
/build/
/dist/
.idea
*.spec

@ -0,0 +1,100 @@
[
{
"key": "{Title}",
"value": "测试标题",
"type": "text"
},
{
"key": "{文件内容}",
"value": "测试内容内容内容",
"type": "text"
},
{
"key": "{TableTitle}",
"value": "这是标题",
"type": "text"
},
{
"key": "{dateTime}",
"value": "2025年11月26日",
"type": "text"
},
{
"key": "{remark}",
"value": "这个是备注备注备注备注",
"type": "text"
},
{
"key": "{Test1}",
"value": "2025年11月26日",
"type": "text"
},
{
"key": "{Test2}",
"value": "2025年11月26日",
"type": "text"
},
{
"key": "{Test3}",
"value": "2025年",
"type": "text"
},
{
"key": "{Picture}",
"value": "rust.jpg",
"type": "picture",
"width": 1.25
},
{
"key": "{Table}",
"type": "table",
"value": [
{
"No": "序号",
"Name": "名称",
"Col2": "第一列",
"Col3": "第二列"
},
{
"No": "1",
"Name": "名称测试1",
"Col2": "列2",
"Col3": "列3"
},
{
"No": "2",
"Name": "名称测试2",
"Col2": "列2",
"Col3": "列3"
},
{
"No": "3",
"Name": "名称测试3",
"Col2": "列2",
"Col3": "列3"
},
{
"No": "4",
"Name": "名称测试4",
"Col2": "列2",
"Col3": "列3"
},
{
"No": "5",
"Name": "名称测试5",
"Col2": "列2",
"Col3": "列3"
}
]
},
{
"key": "{List}",
"type": "list",
"value": [
"A1队列3的生存性最高,机动时间0s,机动距离0m,综合生存能力为0,隐蔽等级为1,道路性质为0;",
"A2队列3的生存性最高,机动时间0s,机动距离0m,综合生存能力为0,隐蔽等级为1,道路性质为0;",
"A3队列3的生存性最高,机动时间0s,机动距离0m,综合生存能力为0,隐蔽等级为1,道路性质为0;",
"A4队列3的生存性最高,机动时间0s,机动距离0m,综合生存能力为0,隐蔽等级为1,道路性质为0;"
]
}
]

Binary file not shown.

@ -0,0 +1,253 @@
import json
import sys
from docx import Document
from docx.shared import Inches
def get_command_argv_by_sys():
# 默认值-用于测试
template_path = "template.docx"
filename_path = "demo.docx"
datafile_path = "data.json"
# 读取参数
number = len(sys.argv)
if 2 == number:
template_path = sys.argv[1]
if 3 == number:
template_path = sys.argv[1]
filename_path = sys.argv[2]
if 4 == number:
template_path = sys.argv[1]
filename_path = sys.argv[2]
datafile_path = sys.argv[3]
return template_path, filename_path, datafile_path
def read_data(filepath):
content = []
if filepath != "":
try:
with open(filepath, "r", encoding="utf-8") as file:
content = json.load(file)
finally:
return content
def replace(template_path, filename_path, data_json):
try:
document_file = Document(template_path)
except Exception:
return 2
# 段落替换
for paragraph in document_file.paragraphs:
paragraph.text = paragraph.text.replace(" ", "")
find = False
for datum in data_json:
key = datum["key"]
if key not in paragraph.text:
continue
find = True
if datum["type"] == "text":
runs = paragraph.runs
for i in range(len(runs)):
if key in runs[i].text:
runs[i].text = runs[i].text.replace(key, str(datum["value"]))
elif datum["type"] == "number":
paragraph.text = paragraph.text.replace(key, str(datum["value"]))
elif datum["type"] == "picture":
# 清空整个段落并插入图片(假设占位符独占段落)
paragraph.clear()
width = float(datum.get("width", 2.0))
paragraph.add_run().add_picture(datum["value"], width=Inches(width))
elif datum["type"] == "list":
p_elem = paragraph._p
parent = p_elem.getparent()
index = parent.index(p_elem)
values = datum["value"]
# 删除原段落
parent.remove(p_elem)
# 为每个值插入新段落
for i, val in enumerate(values):
new_para = document_file.add_paragraph()
new_para.text = str(val)
parent.insert(index + i, new_para._p)
elif datum["type"] == "object_list":
p_elem = paragraph._p
parent = p_elem.getparent()
index = parent.index(p_elem)
values = datum["value"]
# 删除原段落(占位符)
parent.remove(p_elem)
# 为每个对象插入新段落
for i, obj_item in enumerate(values):
new_para = document_file.add_paragraph()
item_type = obj_item.get("type")
if item_type == "text":
new_para.add_run(str(obj_item["value"]))
elif item_type == "number":
new_para.add_run(str(obj_item["value"]))
elif item_type == "picture":
width = float(obj_item.get("width", 2.0))
new_para.add_run().add_picture(obj_item["value"], width=Inches(width))
else:
new_para.add_run(str(obj_item.get("value", "")))
# 插入到原位置
parent.insert(index + i, new_para._p)
if not find:
paragraph.text = ""
# 表格替换
for table in document_file.tables:
# 字段
group = None
fields = []
repeat = False
for row in table.rows:
if repeat:
break
for cell in row.cells:
if "." in cell.text:
split = cell.text.replace("}","").split(".")
group = split[0] + "}"
field = split[1]
fields.append(field.strip())
repeat = True
else:
for paragraph in cell.paragraphs:
for run in paragraph.runs:
find = False
for datum in data_json:
if datum["key"] in cell.text:
find = True
if "text" == datum["type"]:
run.text = run.text.replace(datum["key"], datum["value"])
elif "picture" == datum["type"]:
paragraph = cell.paragraphs[0]
paragraph.clear()
paragraph.add_run().add_picture(datum["value"], width=Inches(datum["width"]))
elif "number" == datum["type"]:
run.text = run.text.replace(datum["key"], str(datum["value"]))
if not find and run.text.startswith("{") and run.text.endswith("}"):
run.text = ""
# 替换
if repeat:
for datum in data_json:
rows = len(table.rows)
if datum["key"] in group:
values = datum["value"]
# 读取表格格式模板(保存每列的格式信息)
column_formats = []
if rows > 0:
template_row = table.rows[0]
for i, cell in enumerate(template_row.cells):
if i < len(fields): # 只需要处理与数据字段对应的列
if cell.paragraphs:
para = cell.paragraphs[0]
# 保存列格式信息
column_format = {
'font_name': '',
'font_size': None,
'alignment': para.alignment,
'bold': False,
'italic': False,
'underline': False,
}
# 优先使用run的格式如果有
if para.runs:
run = para.runs[0]
if run.font.name:
column_format['font_name'] = run.font.name
if run.font.size:
column_format['font_size'] = run.font.size
if run.font.bold is not None:
column_format['bold'] = run.font.bold
if run.font.italic is not None:
column_format['italic'] = run.font.italic
if run.font.underline is not None:
column_format['underline'] = run.font.underline
# 保存字体颜色
if run.font.color.rgb:
column_format['font_color'] = run.font.color.rgb
# 如果没有run使用样式的格式
else:
if para.style.font.name:
column_format['font_name'] = para.style.font.name
if para.style.font.size:
column_format['font_size'] = para.style.font.size
if para.style.font.bold is not None:
column_format['bold'] = para.style.font.bold
if para.style.font.italic is not None:
column_format['italic'] = para.style.font.italic
if para.style.font.underline is not None:
column_format['underline'] = para.style.font.underline
column_formats.append(column_format)
# 处理每一行数据
for (i, item) in enumerate(values):
if i < rows:
row = table.rows[i]
else:
row = table.add_row()
# 复制行高
if i == 0 and rows > 0:
row.height = template_row.height
row.height_rule = template_row.height_rule
# 处理每一列数据和格式
for z in range(0, len(fields)):
field = fields[z]
cell = row.cells[z]
# 设置单元格内容
if item.get(field) is not None:
cell.text = str(item.get(field))
else:
cell.text = ""
# 应用表格格式
if z < len(column_formats) and cell.paragraphs:
format_info = column_formats[z]
para = cell.paragraphs[0]
# 设置段落对齐方式
para.alignment = format_info['alignment']
# 设置字体格式
if para.runs:
run = para.runs[0]
if format_info['font_name']:
run.font.name = format_info['font_name']
if format_info['font_size']:
run.font.size = format_info['font_size']
run.font.bold = format_info['bold']
run.font.italic = format_info['italic']
run.font.underline = format_info['underline']
# 设置字体颜色
if 'font_color' in format_info:
run.font.color.rgb = format_info['font_color']
document_file.save(filename_path)
return 0
def main():
params = get_command_argv_by_sys()
[template, filename, datafile] = params
data = read_data(datafile)
if len(data) == 0:
return 1
else:
return replace(template, filename, data)
if __name__ == '__main__':
print(main())

@ -0,0 +1,43 @@
# 读取模板生成报表
## 打包
1. 打包
> ```powershell
> pyinstaller -F main.py -n doc_generate.exe
> ```
1. 使用
> 在 dist目录下找到可执行文件, 使用命令行调用
> ```python
> ./doc_generate.exe [模板文件] [生成文件] [数据文件]
> ```
1. 使用示例
- Qt
> ```c++
> QString templatefile = "template/template.docx"; // 模板文件
> QString generatefile = "Test.docx"; // 生成文件
> QString datafile = "data/data.json"; // 数据文件
> QString cmd = QString("doc_generate %1 %2 %3").arg(templatefile, generatefile, datafile);
> QProcess process;
> process.start(cmd);
> process.waitForFinished();
> QString output = process.readAllStandardOutput();
> qDebug() << output;
> ```
## 注意
1. 模板文件 使用相对位置,即相对**doc_generate**可执行文件的位置
1. 生成文件 生成的文件都在**doc_generate**所在位置
1. 数据文件 使用相对位置,即相对**doc_generate**可执行文件的位置
2. 数据文件中若有图片路径,则图片路径也应是相对位置,即图片相对**doc_generate**可执行文件的位置
## 返回值说明
| 值 | 说明 |
|:-:|:-------:|
| 0 | 正常 |
| 1 | 数据内容不正确 |
| 2 | 模板文件不正确 |

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.
Loading…
Cancel
Save