先看效果,符不符合你的需求。
背景:
word插入的表格数据填充到该表格时,如果数据量超过该的表格数,应将表格在下一页重新创建该表格,并将剩余数据继续填充到新的表格中。
再简单点:word表格只有5行,但是数据有20行,表格不够添加,就还需要复制3份一样的表格,中间需要分页符隔开,最终得到4份表格。
效果:
用java实现,table是模板,table2是生成的。
table中有1个表格(可以有多个表格,现在演示就只创了1个)
表格可以填写的数据为:{"小武的一天":10,"小李的一天":20,"小赵的一天":12}
填充的数据为:[{"小武的一天":25},{"小李的一天":30},{"小赵的一天":18}]
还需要复制:{"小武的一天":2,"小李的一天":1,"小赵的一天":1}
选择最大值:2就是要复制的数量。
最终table2得到3个表格数量
表格可以填写的数据为:{"小武的一天":10,"小李的一天":20,"小赵的一天":12}
这个10、20、12
是插入的表格数量。
上代码: 依赖需要用我上一个发的,不然操作表格有限制(还有别的限制💥💥),不能操作超过25个表格等等。
http://t.csdnimg.cn/UUICw 看这个获取cracked的jar
pom
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
<!-- POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<!--配合jar包使用;-->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.doc</artifactId>
<version>11.4.2</version>
</dependency>
<!-- jar包-->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spirej.doc.cracked</artifactId>
<version>11.4.2</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version> <!-- 或者使用最新版本 -->
</dependency>
json
{
"id": 184567839137890,
"data": [
{
"name": "名称",
"type": "String",
"value": "小李"
},
{
"name": "年龄",
"type": "String",
"value": "18"
},
{
"name": "身高",
"type": "String",
"value": "181"
},
{
"name": "人员",
"type": "String",
"value": "小李、小武、小赵"
},
{
"name": "日期",
"type": "String",
"value": "2024-08-22"
},
{
"name": "小赵签字",
"type": "String",
"value": "我想睡觉"
},
{
"name": "小武签字",
"type": "String",
"value": "帅名字"
},
{
"name": "小李的一天",
"type": "Row",
"value": [
[
"起床洗漱",
"饭后来一根",
"听歌摸鱼",
"健身",
"emo"
],
[
"起床洗漱",
"饭后来一根",
"听歌摸鱼",
"健身",
"emo"
],
[
"起床洗漱",
"饭后来一根",
"听歌摸鱼",
"健身",
"emo"
],
[
"起床洗漱",
"饭后来一根",
"听歌摸鱼",
"健身",
"emo"
],
[
"起床洗漱",
"饭后来一根",
"听歌摸鱼",
"健身",
"emo"
],
[
"起床洗漱",
"饭后来一根",
"听歌摸鱼",
"健身",
"emo"
]
]
},
{
"name": "小武的一天",
"type": "Row",
"value": [
[
"起床洗漱",
"饭后睡一觉",
"摸鱼",
"diss小赵睡觉",
"diss小赵磨牙"
],
[
"起床洗漱",
"饭后睡一觉",
"摸鱼",
"diss小赵睡觉",
"diss小赵磨牙"
],
[
"起床洗漱",
"饭后睡一觉",
"摸鱼",
"diss小赵睡觉",
"diss小赵磨牙"
],
[
"起床洗漱",
"饭后睡一觉",
"摸鱼",
"diss小赵睡觉",
"diss小赵磨牙"
],
[
"起床洗漱",
"饭后睡一觉",
"摸鱼",
"diss小赵睡觉",
"diss小赵磨牙"
]
]
},
{
"name": "小赵的一天",
"type": "Row",
"value": [
[
"睡觉",
"睡觉",
"睡觉",
"睡觉",
"睡觉",
"睡觉"
],
[
"睡觉",
"睡觉",
"睡觉",
"睡觉",
"睡觉",
"睡觉"
],
[
"睡觉",
"睡觉",
"睡觉",
"睡觉",
"睡觉",
"睡觉"
]
]
}
]
}
javaUtil
json数据在上面,模板自己创一个就行
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.spire.doc.*;
import com.spire.doc.collections.*;
import com.spire.doc.documents.BorderStyle;
import com.spire.doc.documents.BreakType;
import com.spire.doc.documents.Paragraph;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.json.JSONArray;
import org.json.JSONObject;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author HaoLin Xie
* @Date 2024/8/1 22:36
* @PackageName:org.example.utils
* @ClassName: CopyTable
* @Description: 根据数据复制新的表格并添加分页符
* @Version 1.0
*/
public class CopyTable {
public static void main(String[] args) {
String JsonFilePath = "Table.json"; // JSON 文件路径
String path = "Table.docx"; //模板
String file = "Table2.docx"; //生成
//获取传来的json数据
ArrayList<JSONObject> jsonObjectArrayList = jsonDataBase(JsonFilePath);
System.out.println("json-->" + jsonObjectArrayList);
System.out.println();
//创建对象
Document document = new Document();
document.loadFromFile(path);
// 获取文档中的所有节
SectionCollection sections = document.getSections();
// 遍历每个节
for (int secIndex = 0; secIndex < sections.getCount(); secIndex++) {
//获取当前索引的节对象
Section section = sections.get(secIndex);
// 获取节中的所有表格
TableCollection tables = section.getTables();
// 遍历每个表格,并把表格数据放进list
LinkedList<LinkedList> list = BiaoGeSize(tables);
//存放还要复制多少个表格的集合
LinkedList<Integer> linkedListfuzhi = new LinkedList();
//算还要复制多少个表格
for (LinkedList linkedList : list) {
//得到每个表格的数据,把表格需要填充的list 添加占位标识符
insertStartAndEndTags(linkedList);
//获取表格占位符,得到表格的格子数量
JSONObject jsonObjectTable = getzhanweifu(linkedList);
System.out.println("表格:" + jsonObjectTable);
//计算数据的表格数量
JSONArray jsonArrayJsonData = jisuandatabase(jsonObjectArrayList, jsonObjectTable);
System.out.println("数据:" + jsonArrayJsonData);
//计算还要复制多少表格
int remainder = remainder(jsonObjectTable, jsonArrayJsonData);
System.out.println("要复制的表格数: " + remainder);
//把还需要复制多少表格全部放进一个集合
linkedListfuzhi.add(remainder);
System.out.println();
}
//该文档中所有的表格总数
int count = tables.getCount();
System.out.println("当前文档共有表格数量为->" + count);
System.out.println("复制表格list:" + linkedListfuzhi);
//开始复制表格
cloneBiaoGe(tables, count, linkedListfuzhi, section);
int count2 = tables.getCount();
System.out.println("复制后当前文档共有表格数量为->" + count2);
//删除最后1页的分页符
delFenYe(section, sections);
}
//生成
document.saveToFile(file, FileFormat.Docx_2013);
document.dispose();
System.out.println("去除头部警告!");
//去水印(头部警告)
restWord(file);
System.out.println("复制表格完成");
}
/**
* @param table 表格
* @param data json数据
* @return 还要复制的页数
* <p>
* 计算还要复制多少表格
*/
private static int remainder(JSONObject table, JSONArray data) {
// 创建结果的 JSONObject
JSONObject result = new JSONObject();
// 遍历数据
for (int i = 0; i < data.length(); i++) {
org.json.JSONObject item = data.getJSONObject(i);
String key = item.keys().next(); // 获取键
int dataValue = item.getInt(key); // 数据值
int tableValue = table.getInt(key); // 表格值
// 计算商和余数
int quotient = dataValue / tableValue;
int remainder = dataValue % tableValue;
// 根据余数决定是否加一
if (remainder != 0) {
quotient += 1;
}
// 将结果存入 result
result.put(key, quotient - 1);
}
// 打印结果
System.out.println("还需要复制:" + result.toString());
// 初始化最大值和对应的键
int maxValue = 0;
for (String key : result.keySet()) {
int value = result.getInt(key);
if (value > maxValue) {
maxValue = value;
}
}
//返回最大值
return maxValue;
}
/**
* @param jsonFilePath json路径
* @return 获取传来的json数据
*
* 获取传来的json数据
*/
private static ArrayList<JSONObject> jsonDataBase(String jsonFilePath) {
// 读取 JSON 文件内容
ArrayList<JSONObject> jsonObjectArrayList = new ArrayList<>();
try (FileReader reader = new FileReader(jsonFilePath)) {
JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
JsonArray dataArray = jsonObject.getAsJsonArray("data");
// 处理 JSON 数据
for (int i = 0; i < dataArray.size(); i++) {
JsonObject item = dataArray.get(i).getAsJsonObject();
if (item.get("type").getAsString().equals("Row")) {
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("name", item.get("name").getAsString());
jsonObject1.put("type", item.get("type").getAsString());
jsonObject1.put("value", item.get("value").toString());
jsonObjectArrayList.add(jsonObject1);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return jsonObjectArrayList;
}
/**
* @param section 节对象
* @param sections 所有节
* <p>
* 删除最后1页的分页符
*/
private static void delFenYe(Section section, SectionCollection sections) {
Paragraph lastParagraph = section.getParagraphs().get(section.getParagraphs().getCount() - 1);
// 检查最后一页是否为空白
if (lastParagraph.getText().trim().isEmpty()) {
// 删除最后一页的段落或内容
section.getParagraphs().remove(lastParagraph);
// 如果需要删除最后一页的章节(如果它是唯一的章节且为空)
if (section.getParagraphs() == null) {
sections.remove(section);
}
}
}
/**
* @param jsonObjectArrayList json数据
* @param getzhanweifu 上一步算出的表格的名称和长度
* @return 数据的名称和长度
* <p>
* 计算数据的长度
*/
private static JSONArray jisuandatabase(ArrayList<JSONObject> jsonObjectArrayList, JSONObject getzhanweifu) {
List<JSONObject> matchedObjects = new ArrayList<>();
for (String key : getzhanweifu.keySet()) {
for (JSONObject jsonObject : jsonObjectArrayList) {
if (jsonObject.getString("name").equals(key)) {
matchedObjects.add(jsonObject);
break;
}
}
}
JSONArray jsonArray = new JSONArray();
for (JSONObject obj : matchedObjects) {
String name = obj.getString("name");
String valueString = obj.getString("value");
JSONArray outerArray = new JSONArray(valueString);
//初始化
int totalLength = 0;
for (int i = 0; i < outerArray.length(); i++) {
JSONArray innerArray = outerArray.getJSONArray(i);
totalLength += innerArray.length();
}
JSONObject resultJson = new JSONObject();
resultJson.put(name, totalLength);
jsonArray.put(resultJson);
}
return jsonArray;
}
/**
* @param es 每个表格
* <p>
* 给表格自动添加占位符
*/
private static void insertStartAndEndTags(LinkedList es) {
List<Integer> arrList = new LinkedList<>();
for (int i = 0; i < es.size(); i++) {
if (es.get(i).equals("")) {
if (!es.get(i - 1).equals("") && !es.get(i - 1).equals("${start}")) {
es.set(i, "${start}");
}
if (!es.get(i + 1).equals("") && !es.get(i + 1).equals("${end}")) {
es.set(i, "${end}");
} else {
es.set(es.size() - 1, "${end}");
}
}
}
for (int j = 0; j < es.size(); j++) {
if (es.get(j).equals("${start}") || es.get(j).equals("${end}")) {
if (es.get(j).equals("${end}") && es.size() - 1 == j && !es.get(j - 1).equals("")) {
arrList.add(j);
} else if (!es.get(j - 1).equals("") && !es.get(j + 1).equals("")) {
arrList.add(j);
}
}
}
for (int k = 0; k < arrList.size(); k++) {
es.set(arrList.get(k), "");
}
}
/**
* @param list 每个表格
* @return 返回文档内表格的名称和格子长度
*
* 获取占位符的下标
*/
private static JSONObject getzhanweifu(LinkedList list) {
System.out.println("修改后的-->" + list);
//获取表格内填充表格的名字
LinkedList<String> stringLinkedList = FindAllListsName(list);
LinkedList<Integer> startIndices = new LinkedList(); //开始
LinkedList<Integer> endIndices = new LinkedList(); //结束
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("${start}")) {
startIndices.add(i);
} else if (list.get(i).equals("${end}")) {
endIndices.add(i);
}
}
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < Math.min(startIndices.size(), endIndices.size()); i++) {
int startIndex = startIndices.get(i);
int endIndex = endIndices.get(i);
linkedList.add((endIndex - startIndex) + 1);
//System.out.println("第 " + (i + 1) + " 对: ${start} 位置 = " + startIndex + ", ${end} 位置 = " + endIndex + ", 有" + ((endIndex - startIndex) + 1) + "个");
}
// 创建 JSONObject
JSONObject jsonObject = new JSONObject();
for (int i = 0; i < stringLinkedList.size(); i++) {
jsonObject.put(stringLinkedList.get(i), linkedList.get(i));
}
return jsonObject;
}
/**
* @param list 修改后的list
* @return
*
* 获取表格内填充表格的名字
*/
private static LinkedList<String> FindAllListsName(LinkedList list) {
LinkedList<String> stringLinkedList = new LinkedList<>();
// 将 LinkedList 转换为一个字符串
String data = String.join("", list);
// 正则表达式模式用于匹配 ${} 标记
Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");
Matcher matcher = pattern.matcher(data);
// 存储所有 `${start}` 的位置
LinkedList<Integer> startPositions = new LinkedList<>();
while (matcher.find()) {
if (matcher.group().equals("${start}")) {
startPositions.add(matcher.start());
}
}
// 如果没有找到 `${start}` 标记
if (startPositions.isEmpty()) {
System.out.println("没有找到 `${start}` 标记。");
return null;
}
// 再次匹配所有 `${}` 标记
matcher = pattern.matcher(data);
String lastMarker = "";
int lastMarkerPos = -1;
// 遍历所有 `${start}` 的位置
for (int startPos : startPositions) {
// 查找最近的 `${}` 标记
while (matcher.find()) {
String marker = matcher.group();
if (matcher.start() >= startPos) {
break;
}
lastMarker = marker;
lastMarkerPos = matcher.start();
}
// 提取 `${start}` 前面最近的 `${}` 包住的内容
if (!lastMarker.isEmpty() && lastMarkerPos != -1) {
int lastMarkerEndPos = lastMarkerPos + lastMarker.length();
String result = data.substring(lastMarkerPos + 2, lastMarkerEndPos - 1); // 提取 `${}` 包住的内容
stringLinkedList.add(result);
} else {
System.out.println("`${start}` 之前没有找到 `${}` 标记。");
}
// 重置 `Matcher` 对象,以便继续查找下一个 `${start}`
matcher = pattern.matcher(data);
}
return stringLinkedList;
}
/**
* @param tables 所有表格
* @return 获取表格下标,遍历表格内
*/
private static LinkedList<LinkedList> BiaoGeSize(TableCollection tables) {
LinkedList<LinkedList> es2 = new LinkedList();
for (int tableIndex = 0; tableIndex < tables.getCount(); tableIndex++) {
LinkedList es1 = new LinkedList<>();
Table table = tables.get(tableIndex);
// 遍历表格中的行
RowCollection rows = table.getRows();
for (int rowIndex = 0; rowIndex < rows.getCount(); rowIndex++) {
TableRow row = rows.get(rowIndex);
CellCollection cells = row.getCells();
for (int cellIndex = 0; cellIndex < cells.getCount(); cellIndex++) {
//内容
TableCell cell = cells.get(cellIndex);
String cellText = cell.getParagraphs().get(0).getText();
es1.add(cellText);
}
}
es2.add(es1);
}
return es2;
}
/**
* @param tables 所有表格
* @param count 表格总数
* @param fuzhibiaoge 要复制多少表格的Lsit
* @param section 节对象
* <p>
* 复制表格
*/
private static void cloneBiaoGe(TableCollection tables, int count, LinkedList<Integer> fuzhibiaoge, Section section) {
for (int j = 0; j < count; j++) {
// 处理每个表格 必须是表格的下标
Table table2 = tables.get(j);
// 获取表格的行数和列数
int rowCount = table2.getRows().getCount(); //行
//int columnCount = table2.getRows().get(0).getCells().getCount(); //列
System.out.println("表格的行数:" + rowCount);
for (int n = 0; n < fuzhibiaoge.size(); n++) {
if (j == n) {
for (int k = 0; k < fuzhibiaoge.get(n); k++) {
//创建一个新表格
Table table = addTable(section);
section.addParagraph().appendBreak(BreakType.Page_Break);
for (int i = 0; i < rowCount; i++) {
TableRow row1 = table2.getRows().get(i).deepClone();
table.getRows().insert(i, row1); //放进新表格
}
table.autoFit(AutoFitBehaviorType.Fixed_Column_Widths);
}
}
}
}
}
/**
* @param section 每一节
* @return
* 创建一个新表格
*/
private static Table addTable(Section section) {
//创建一个新表格
Table table1 = section.addTable(true);
//黑色
table1.getTableFormat().getBorders().getTop().setColor(Color.BLACK);
table1.getTableFormat().getBorders().getBottom().setColor(Color.BLACK);
table1.getTableFormat().getBorders().getLeft().setColor(Color.BLACK);
table1.getTableFormat().getBorders().getRight().setColor(Color.BLACK);
//粗边框
table1.getTableFormat().getBorders().getTop().setBorderType(BorderStyle.Hairline);
table1.getTableFormat().getBorders().getBottom().setBorderType(BorderStyle.Hairline);
table1.getTableFormat().getBorders().getLeft().setBorderType(BorderStyle.Hairline);
table1.getTableFormat().getBorders().getRight().setBorderType(BorderStyle.Hairline);
//边框大小
float size = 2.5F;
table1.getTableFormat().getBorders().getTop().setLineWidth(size);
table1.getTableFormat().getBorders().getBottom().setLineWidth(size);
table1.getTableFormat().getBorders().getLeft().setLineWidth(size);
table1.getTableFormat().getBorders().getRight().setLineWidth(size);
return table1;
}
/**
* 使用spire.Doc 会出现一个水印(头部警告)可以通过这个方法去除水印
*
* @param docFilePath 生成的新路径
*/
private static void restWord(String docFilePath) {
try (FileInputStream in = new FileInputStream(docFilePath)) {
XWPFDocument doc = new XWPFDocument(OPCPackage.open(in));
List<XWPFParagraph> paragraphs = doc.getParagraphs();
if (paragraphs.size() < 1) {
return;
}
XWPFParagraph firstParagraph = paragraphs.get(0);
if (firstParagraph.getText().contains("Spire.Doc")) {
doc.removeBodyElement(doc.getPosOfParagraph(firstParagraph));
}
OutputStream out = new FileOutputStream(docFilePath);
doc.write(out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
三克油