|
@@ -1,4 +1,5 @@
|
|
|
package com.yaoyicloud.tools;
|
|
|
+
|
|
|
import com.lowagie.text.Image;
|
|
|
import com.lowagie.text.PageSize;
|
|
|
import com.lowagie.text.pdf.BaseFont;
|
|
@@ -43,8 +44,17 @@ import java.util.regex.Matcher;
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
public class OfficeUtil {
|
|
|
- private static final org.slf4j.Logger OFFICE_UTIL_LOGGER = org.slf4j.LoggerFactory.getLogger(OfficeUtil.class);
|
|
|
+ private static final org.slf4j.Logger OFFICE_UTIL_LOGGER = org.slf4j.LoggerFactory.getLogger(OfficeUtil.class);
|
|
|
private static Map<String, Integer> pageNumberMap = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 把word 转为html
|
|
|
+ *
|
|
|
+ * @param docxPath word文档路径
|
|
|
+ * @param imageDir 图片文件夹路径
|
|
|
+ * @return
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
public static String convert(String docxPath, String imageDir) throws IOException {
|
|
|
File imageDirFile = new File(imageDir);
|
|
|
if (!imageDirFile.exists() && !imageDirFile.mkdirs()) {
|
|
@@ -52,8 +62,8 @@ public class OfficeUtil {
|
|
|
}
|
|
|
|
|
|
try (InputStream docxIn = new FileInputStream(docxPath);
|
|
|
- XWPFDocument document = new XWPFDocument(docxIn);
|
|
|
- ByteArrayOutputStream htmlOut = new ByteArrayOutputStream()) {
|
|
|
+ XWPFDocument document = new XWPFDocument(docxIn);
|
|
|
+ ByteArrayOutputStream htmlOut = new ByteArrayOutputStream()) {
|
|
|
|
|
|
// 1. 遍历所有表格,检查是否需要删除
|
|
|
List<XWPFTable> tablesToRemove = new ArrayList<>();
|
|
@@ -78,16 +88,16 @@ public class OfficeUtil {
|
|
|
};
|
|
|
|
|
|
XHTMLOptions options = XHTMLOptions.create()
|
|
|
- .setImageManager(imageManager)
|
|
|
- .URIResolver(new FileURIResolver(imageDirFile) {
|
|
|
- @Override
|
|
|
- public String resolve(String uri) {
|
|
|
- String filename = uri.replace("word/media/", "");
|
|
|
- return new File(imageDirFile, filename)
|
|
|
- .getAbsolutePath()
|
|
|
- .replace("/", "\\");
|
|
|
- }
|
|
|
- });
|
|
|
+ .setImageManager(imageManager)
|
|
|
+ .URIResolver(new FileURIResolver(imageDirFile) {
|
|
|
+ @Override
|
|
|
+ public String resolve(String uri) {
|
|
|
+ String filename = uri.replace("word/media/", "");
|
|
|
+ return new File(imageDirFile, filename)
|
|
|
+ .getAbsolutePath()
|
|
|
+ .replace("/", "\\");
|
|
|
+ }
|
|
|
+ });
|
|
|
options.setIgnoreStylesIfUnused(false);
|
|
|
|
|
|
XHTMLConverter.getInstance().convert(document, htmlOut, options);
|
|
@@ -148,30 +158,30 @@ public class OfficeUtil {
|
|
|
}
|
|
|
|
|
|
String baseCss =
|
|
|
- "@page {"
|
|
|
- + " size: A4;"
|
|
|
- + " @bottom-center {"
|
|
|
- + " content: none;" // 只显示数字页码
|
|
|
- + " }"
|
|
|
- + "}"
|
|
|
- + "@page show-page-number {"
|
|
|
- + " @bottom-center {"
|
|
|
- + " content: counter(page);"
|
|
|
- + " font-family: 思源黑体 Medium;"
|
|
|
- + " font-size: 9pt;"
|
|
|
- + " color: #000000;"
|
|
|
- + " }"
|
|
|
- + "}"
|
|
|
- + // 为最后一个div设置页码显示并重置计数器
|
|
|
- ".start-counting {"
|
|
|
- + " page: show-page-number;"
|
|
|
- + "}"
|
|
|
- + "td, th { "
|
|
|
- + " page-break-inside: avoid; " // 尽量保持单元格不分页
|
|
|
- + " -fs-table-paginate: paginate; " // 允许分页
|
|
|
- + " background-clip: padding-box; " // 确保背景色覆盖
|
|
|
- + " -webkit-print-color-adjust: exact; " // 确保打印时颜色准确
|
|
|
- + "}";
|
|
|
+ "@page {"
|
|
|
+ + " size: A4;"
|
|
|
+ + " @bottom-center {"
|
|
|
+ + " content: none;" // 只显示数字页码
|
|
|
+ + " }"
|
|
|
+ + "}"
|
|
|
+ + "@page show-page-number {"
|
|
|
+ + " @bottom-center {"
|
|
|
+ + " content: counter(page);"
|
|
|
+ + " font-family: 思源黑体 Medium;"
|
|
|
+ + " font-size: 9pt;"
|
|
|
+ + " color: #000000;"
|
|
|
+ + " }"
|
|
|
+ + "}"
|
|
|
+ + // 为最后一个div设置页码显示并重置计数器
|
|
|
+ ".start-counting {"
|
|
|
+ + " page: show-page-number;"
|
|
|
+ + "}"
|
|
|
+ + "td, th { "
|
|
|
+ + " page-break-inside: avoid; " // 尽量保持单元格不分页
|
|
|
+ + " -fs-table-paginate: paginate; " // 允许分页
|
|
|
+ + " background-clip: padding-box; " // 确保背景色覆盖
|
|
|
+ + " -webkit-print-color-adjust: exact; " // 确保打印时颜色准确
|
|
|
+ + "}";
|
|
|
Elements table = doc.select("table");
|
|
|
String tbaleStyle = table.attr("style");
|
|
|
tbaleStyle += "width:100%;";
|
|
@@ -186,44 +196,6 @@ public class OfficeUtil {
|
|
|
}
|
|
|
doc.head().appendElement("style").text(baseCss);
|
|
|
|
|
|
-// int groupId = 0;
|
|
|
-// for (Element row : doc.select("tr:has(td[rowspan], th[rowspan])")) {
|
|
|
-// int rowspan = Integer.parseInt(row.select("[rowspan]").first().attr("rowspan"));
|
|
|
-// row.attr("data-group-id", String.valueOf(groupId++));
|
|
|
-//
|
|
|
-// // 标记被rowspan覆盖的行
|
|
|
-// Element nextRow = row.nextElementSibling();
|
|
|
-// for (int i = 1; i < rowspan && nextRow != null; i++) {
|
|
|
-// nextRow.attr("data-group-child", "true");
|
|
|
-// nextRow = nextRow.nextElementSibling();
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// doc.head().appendElement("style")
|
|
|
-// .text("tr[data-group-id], tr[data-group-child] { "
|
|
|
-// + "page-break-inside: avoid !important; "
|
|
|
-// + "}");
|
|
|
-
|
|
|
-// //合并单元格的处理
|
|
|
-// Elements rowsWithRowspan = doc.select("tr:has(td[rowspan], th[rowspan])");
|
|
|
-//
|
|
|
-// // 遍历所有包含合并单元格的行
|
|
|
-// for (Element row : rowsWithRowspan) {
|
|
|
-// // 找到合并单元格的跨行数
|
|
|
-// int rowspan = Integer.parseInt(row.select("td[rowspan], th[rowspan]").first().attr("rowspan"));
|
|
|
-//
|
|
|
-// // 创建新的 tbody 包裹当前行及后续受影响的行
|
|
|
-// Element tbody = new Element("tbody").attr("style", "page-break-inside: avoid; width: 100%;");
|
|
|
-// for (int i = 0; i < rowspan; i++) {
|
|
|
-// Element nextRow = row.nextElementSibling();
|
|
|
-// row.before(tbody);
|
|
|
-// tbody.appendChild(row);
|
|
|
-// if (nextRow != null) {
|
|
|
-// row = nextRow; // 处理后续行
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
Elements tds = doc.select("td");
|
|
|
for (Element td : tds) {
|
|
|
Elements ps = td.select("p");
|
|
@@ -231,7 +203,7 @@ public class OfficeUtil {
|
|
|
String originalStyle = p.attr("style");
|
|
|
// 添加新样式,保留原有样式但覆盖冲突属性
|
|
|
String newStyle = "margin-left: 0.5em; margin-right: 0.5em; "
|
|
|
- + "line-height: 1.2; margin-top: 6px!important; margin-bottom: 6px!important; " + originalStyle;
|
|
|
+ + "line-height: 1.2; margin-top: 6px!important; margin-bottom: 6px!important; " + originalStyle;
|
|
|
p.attr("style", newStyle);
|
|
|
}
|
|
|
if (ps.size() > 1) {
|
|
@@ -308,7 +280,7 @@ public class OfficeUtil {
|
|
|
|
|
|
}
|
|
|
|
|
|
- //二级标题上边距调整
|
|
|
+ // 二级标题上边距调整
|
|
|
Elements secondaryHeadingStyle = doc.select("p.X1.X3");
|
|
|
for (Element secondaryelement : secondaryHeadingStyle) {
|
|
|
|
|
@@ -318,16 +290,18 @@ public class OfficeUtil {
|
|
|
} else {
|
|
|
secondarycurrentStyle += " margin-top: 13pt;";
|
|
|
}
|
|
|
- secondaryelement.attr("style", secondarycurrentStyle + "line-height: 1.5; margin-bottom: 1pt; margin-left: 0.5em");
|
|
|
+ secondaryelement.attr("style",
|
|
|
+ secondarycurrentStyle + "line-height: 1.5; margin-bottom: 1pt; margin-left: 0.5em");
|
|
|
}
|
|
|
|
|
|
- //三级标题样式
|
|
|
+ // 三级标题样式
|
|
|
Elements otherElements = doc.select("p.X1.X4");
|
|
|
for (Element element : otherElements) {
|
|
|
String style3 = element.attr("style");
|
|
|
- element.attr("style", style3 + "margin-top: 1pt !important; margin-bottom: 0pt !important; margin-left: 0.5em");
|
|
|
+ element.attr("style",
|
|
|
+ style3 + "margin-top: 1pt !important; margin-bottom: 0pt !important; margin-left: 0.5em");
|
|
|
}
|
|
|
- //六级标题样式
|
|
|
+ // 六级标题样式
|
|
|
Elements select1 = doc.select("p.X1.X6");
|
|
|
for (Element element : select1) {
|
|
|
element.attr("style", element.attr("style") + "line-height: 1.5; margin-top: 5pt; margin-bottom: 5pt;");
|
|
@@ -336,16 +310,18 @@ public class OfficeUtil {
|
|
|
for (Element element : select5) {
|
|
|
element.attr("style", element.attr("style") + "line-height: 1.5; margin-top: 5pt; margin-bottom: 5pt;");
|
|
|
}
|
|
|
- //超链接
|
|
|
+ // 超链接
|
|
|
Elements select9 = doc.select("p.X1.X9");
|
|
|
for (Element element : select9) {
|
|
|
- element.attr("style", element.attr("style") + "word-break: break-all; overflow-wrap: anywhere; max-width: 100%;");
|
|
|
+ element.attr("style",
|
|
|
+ element.attr("style") + "word-break: break-all; overflow-wrap: anywhere; max-width: 100%;");
|
|
|
}
|
|
|
Elements select8 = doc.select("p.X1.X8");
|
|
|
for (Element element : select8) {
|
|
|
- element.attr("style", element.attr("style") + "word-break: break-all; overflow-wrap: anywhere; max-width: 100%;");
|
|
|
+ element.attr("style",
|
|
|
+ element.attr("style") + "word-break: break-all; overflow-wrap: anywhere; max-width: 100%;");
|
|
|
}
|
|
|
- //1.3合并的单元格 不分页
|
|
|
+ // 1.3合并的单元格 不分页
|
|
|
Elements select11 = doc.select("td.X10.X11");
|
|
|
for (Element element : select11) {
|
|
|
element.attr("style", element.attr("style") + "page-break-inside: avoid;");
|
|
@@ -359,8 +335,6 @@ public class OfficeUtil {
|
|
|
return doc.html();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
/**
|
|
|
* 合并表格中相同内容的单元格
|
|
|
*
|
|
@@ -401,7 +375,7 @@ public class OfficeUtil {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 移除 white-space:pre-wrap 并替换为 normal
|
|
|
+ * 移除 white-space:pre-wrap 并替换为 normal
|
|
|
*/
|
|
|
private static String removeWhiteSpacePreWrap(String style) {
|
|
|
if (style == null) {
|
|
@@ -415,98 +389,110 @@ public class OfficeUtil {
|
|
|
}
|
|
|
return style.trim();
|
|
|
}
|
|
|
+
|
|
|
/**
|
|
|
* 添加目录
|
|
|
+ *
|
|
|
* @param doc
|
|
|
*/
|
|
|
private static void addTableOfContents(Document doc) {
|
|
|
|
|
|
- // 目录样式
|
|
|
- String tocCss = ".toc-container { margin: 20px 0; font-family: 思源黑体 Medium; }"
|
|
|
- + ".toc-title { text-align: center; font-size: 12pt; margin-bottom: 15px; color: black; }"
|
|
|
- + ".toc-list { list-style-type: none; padding: 0; width: 100%; }"
|
|
|
- + ".toc-item { margin: 5px 0; padding-top: 2px; padding-bottom: 2px; line-height: 2; }"
|
|
|
- + ".toc-level-1 { padding-left: 0; }"
|
|
|
- + ".toc-level-2 { padding-left: 2em; }"
|
|
|
- + ".toc-link { "
|
|
|
- + " display: block; "
|
|
|
- + " position: relative; "
|
|
|
- + " color: black !important; "
|
|
|
- + " text-decoration: none !important; "
|
|
|
- + " line-height: 1.5; " // 新增:控制整体行高
|
|
|
- + "}"
|
|
|
- + ".toc-line-container { "
|
|
|
- + " display: table; "
|
|
|
- + " width: 100%; "
|
|
|
- + " vertical-align: middle; " // 关键:控制容器内垂直对齐
|
|
|
- + "}"
|
|
|
- + ".toc-text { "
|
|
|
- + " display: table-cell; "
|
|
|
- + " font-size: 9pt; "
|
|
|
- + " white-space: nowrap; "
|
|
|
- + " padding-right: 5px; "
|
|
|
- + " vertical-align: middle; " // 改为middle对齐
|
|
|
- + "}"
|
|
|
- + ".toc-dots { "
|
|
|
- + " display: table-cell; "
|
|
|
- + " width: 100%; "
|
|
|
- + " vertical-align: middle; " // 关键:改为middle对齐
|
|
|
- + " border-bottom: 1px dotted #000000; "
|
|
|
- + " height: 1em; " // 固定高度
|
|
|
- + " margin-top: 2px; " // 关键:正值下移,负值上移(按需调整)
|
|
|
- + "}"
|
|
|
- + "p.X1.X2 { -fs-pdf-bookmark: level 1; }"
|
|
|
- + "p.X1.X3 { -fs-pdf-bookmark: level 2; }"
|
|
|
- + ".toc-page { "
|
|
|
- + " display: table-cell; "
|
|
|
- + " font-size: 9pt; "
|
|
|
- + " white-space: nowrap; "
|
|
|
- + " padding-left: 5px; "
|
|
|
- + " vertical-align: middle; " // 改为middle对齐
|
|
|
- + "}";
|
|
|
- doc.head().appendElement("style").text(tocCss);
|
|
|
-
|
|
|
- // 构建目录内容
|
|
|
- Element tocList = new Element("ul").addClass("toc-list");
|
|
|
- doc.select("p.X1.X2, p.X1.X3").forEach(el -> {
|
|
|
- boolean isLevel1 = el.hasClass("X2");
|
|
|
- String id = "sec_" + el.text().hashCode();
|
|
|
- el.attr("id", id);
|
|
|
- Integer pageNumber = pageNumberMap.getOrDefault(el.text(), 1);
|
|
|
-
|
|
|
- Element li = tocList.appendElement("li")
|
|
|
- .addClass("toc-item " + (isLevel1 ? "toc-level-1" : "toc-level-2"));
|
|
|
-
|
|
|
- Element link = li.appendElement("a")
|
|
|
- .attr("href", "#" + id)
|
|
|
- .addClass("toc-link");
|
|
|
- Element lineContainer = link.appendElement("div").addClass("toc-line-container");
|
|
|
- lineContainer.appendElement("span").addClass("toc-text").text(el.text());
|
|
|
- lineContainer.appendElement("span").addClass("toc-dots");
|
|
|
- lineContainer.appendElement("span").addClass("toc-page").text(String.valueOf(pageNumber));
|
|
|
- });
|
|
|
-
|
|
|
- // 插入目录
|
|
|
- Element firstDiv = doc.select("div").first();
|
|
|
- if (firstDiv != null) {
|
|
|
- firstDiv.after(
|
|
|
- "<div class='toc-container' style='page-break-before: always;'>"
|
|
|
- + "<h1 class='toc-title'>目录</h1>"
|
|
|
- + tocList.outerHtml()
|
|
|
- + "</div>"
|
|
|
- );
|
|
|
- } else {
|
|
|
- doc.body().prepend(
|
|
|
- "<div class='toc-container' style='page-break-before: always;'>"
|
|
|
- + "<h1 class='toc-title'>目录</h1>"
|
|
|
- + tocList.outerHtml()
|
|
|
- + "</div>"
|
|
|
- );
|
|
|
- }
|
|
|
+ // 目录样式
|
|
|
+ String tocCss = ".toc-container { margin: 20px 0; font-family: 思源黑体 Medium; }"
|
|
|
+ + ".toc-title { text-align: center; font-size: 12pt; margin-bottom: 15px; color: black; }"
|
|
|
+ + ".toc-list { list-style-type: none; padding: 0; width: 100%; }"
|
|
|
+ + ".toc-item { margin: 5px 0; padding-top: 2px; padding-bottom: 2px; line-height: 2; }"
|
|
|
+ + ".toc-level-1 { padding-left: 0; }"
|
|
|
+ + ".toc-level-2 { padding-left: 2em; }"
|
|
|
+ + ".toc-link { "
|
|
|
+ + " display: block; "
|
|
|
+ + " position: relative; "
|
|
|
+ + " color: black !important; "
|
|
|
+ + " text-decoration: none !important; "
|
|
|
+ + " line-height: 1.5; " // 新增:控制整体行高
|
|
|
+ + "}"
|
|
|
+ + ".toc-line-container { "
|
|
|
+ + " display: table; "
|
|
|
+ + " width: 100%; "
|
|
|
+ + " vertical-align: middle; " // 关键:控制容器内垂直对齐
|
|
|
+ + "}"
|
|
|
+ + ".toc-text { "
|
|
|
+ + " display: table-cell; "
|
|
|
+ + " font-size: 9pt; "
|
|
|
+ + " white-space: nowrap; "
|
|
|
+ + " padding-right: 5px; "
|
|
|
+ + " vertical-align: middle; " // 改为middle对齐
|
|
|
+ + "}"
|
|
|
+ + ".toc-dots { "
|
|
|
+ + " display: table-cell; "
|
|
|
+ + " width: 100%; "
|
|
|
+ + " vertical-align: middle; " // 关键:改为middle对齐
|
|
|
+ + " border-bottom: 1px dotted #000000; "
|
|
|
+ + " height: 1em; " // 固定高度
|
|
|
+ + " margin-top: 2px; " // 关键:正值下移,负值上移(按需调整)
|
|
|
+ + "}"
|
|
|
+ + "p.X1.X2 { -fs-pdf-bookmark: level 1; }"
|
|
|
+ + "p.X1.X3 { -fs-pdf-bookmark: level 2; }"
|
|
|
+ + ".toc-page { "
|
|
|
+ + " display: table-cell; "
|
|
|
+ + " font-size: 9pt; "
|
|
|
+ + " white-space: nowrap; "
|
|
|
+ + " padding-left: 5px; "
|
|
|
+ + " vertical-align: middle; " // 改为middle对齐
|
|
|
+ + "}";
|
|
|
+ doc.head().appendElement("style").text(tocCss);
|
|
|
+
|
|
|
+ // 构建目录内容
|
|
|
+ Element tocList = new Element("ul").addClass("toc-list");
|
|
|
+ doc.select("p.X1.X2, p.X1.X3").forEach(el -> {
|
|
|
+ boolean isLevel1 = el.hasClass("X2");
|
|
|
+ String id = "sec_" + el.text().hashCode();
|
|
|
+ el.attr("id", id);
|
|
|
+ Integer pageNumber = pageNumberMap.getOrDefault(el.text(), 1);
|
|
|
+
|
|
|
+ Element li = tocList.appendElement("li")
|
|
|
+ .addClass("toc-item " + (isLevel1 ? "toc-level-1" : "toc-level-2"));
|
|
|
+
|
|
|
+ Element link = li.appendElement("a")
|
|
|
+ .attr("href", "#" + id)
|
|
|
+ .addClass("toc-link");
|
|
|
+ Element lineContainer = link.appendElement("div").addClass("toc-line-container");
|
|
|
+ lineContainer.appendElement("span").addClass("toc-text").text(el.text());
|
|
|
+ lineContainer.appendElement("span").addClass("toc-dots");
|
|
|
+ lineContainer.appendElement("span").addClass("toc-page").text(String.valueOf(pageNumber));
|
|
|
+ });
|
|
|
+
|
|
|
+ // 插入目录
|
|
|
+ Element firstDiv = doc.select("div").first();
|
|
|
+ if (firstDiv != null) {
|
|
|
+ firstDiv.after(
|
|
|
+ "<div class='toc-container' style='page-break-before: always;'>"
|
|
|
+ + "<h1 class='toc-title'>目录</h1>"
|
|
|
+ + tocList.outerHtml()
|
|
|
+ + "</div>");
|
|
|
+ } else {
|
|
|
+ doc.body().prepend(
|
|
|
+ "<div class='toc-container' style='page-break-before: always;'>"
|
|
|
+ + "<h1 class='toc-title'>目录</h1>"
|
|
|
+ + tocList.outerHtml()
|
|
|
+ + "</div>");
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- public static String convertHtmlToPdf(String html, String outputPdfPath, List<String> fontPaths, String imagePath, boolean flag) throws Exception {
|
|
|
+ /**
|
|
|
+ * 将 HTML 内容转换为 PDF 文件
|
|
|
+ *
|
|
|
+ * @param html 要转换的 HTML 字符串内容
|
|
|
+ * @param outputPdfPath 生成 PDF 文件的输出路径
|
|
|
+ * @param fontPaths 字体文件路径列表,用于解决 PDF 中文显示等字体问题
|
|
|
+ * @param imagePath 图片相关路径,用于后续 PDF 处理(如添加水印等操作)
|
|
|
+ * @param flag 标志位,用于控制后续 PDF 处理的逻辑(如是否添加特定内容等)
|
|
|
+ * @return 处理后最终的 PDF 文件路径
|
|
|
+ * @throws Exception 转换过程中出现异常时抛出
|
|
|
+ */
|
|
|
+ public static String convertHtmlToPdf(String html, String outputPdfPath, List<String> fontPaths, String imagePath,
|
|
|
+ boolean flag) throws Exception {
|
|
|
try (OutputStream os = new FileOutputStream(outputPdfPath)) {
|
|
|
ITextRenderer renderer = new ITextRenderer();
|
|
|
ITextFontResolver fontResolver = renderer.getFontResolver();
|
|
@@ -524,21 +510,19 @@ public class OfficeUtil {
|
|
|
}
|
|
|
// 字体路径
|
|
|
fontResolver.addFont(
|
|
|
- mediumFont,
|
|
|
- "思源黑体 Medium",
|
|
|
- BaseFont.IDENTITY_H,
|
|
|
- true,
|
|
|
- null
|
|
|
- );
|
|
|
+ mediumFont,
|
|
|
+ "思源黑体 Medium",
|
|
|
+ BaseFont.IDENTITY_H,
|
|
|
+ true,
|
|
|
+ null);
|
|
|
fontResolver.addFont(
|
|
|
- mediumFont,
|
|
|
- "Arial",
|
|
|
- BaseFont.IDENTITY_H,
|
|
|
- true,
|
|
|
- null
|
|
|
- );
|
|
|
+ mediumFont,
|
|
|
+ "Arial",
|
|
|
+ BaseFont.IDENTITY_H,
|
|
|
+ true,
|
|
|
+ null);
|
|
|
html = html.replace("C:\\", "file:///C:/")
|
|
|
- .replace("\\", "/");
|
|
|
+ .replace("\\", "/");
|
|
|
// 设置HTML(确保CSS中使用相同的font-family)
|
|
|
renderer.setDocumentFromString(html, "file:///");
|
|
|
// 渲染PDF
|
|
@@ -561,22 +545,23 @@ public class OfficeUtil {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 操作已生成的pdf
|
|
|
- * @param inputPdfPath 输入pdf
|
|
|
- * @param outputPdfPath 输出pdf
|
|
|
- * @param backgroundImagePath 图片文件夹位置
|
|
|
+ * 操作已生成的pdf
|
|
|
+ *
|
|
|
+ * @param inputPdfPath 输入pdf
|
|
|
+ * @param outputPdfPath 输出pdf
|
|
|
+ * @param backgroundImagePath 图片文件夹位置
|
|
|
* @param onlyCollectPageNumbers 是否是遍历目录获取标题位置
|
|
|
* @throws Exception
|
|
|
*/
|
|
|
private static void pdfReader(String inputPdfPath, String outputPdfPath,
|
|
|
- String backgroundImagePath, boolean onlyCollectPageNumbers)
|
|
|
- throws Exception {
|
|
|
+ String backgroundImagePath, boolean onlyCollectPageNumbers)
|
|
|
+ throws Exception {
|
|
|
PdfReader reader = new PdfReader(inputPdfPath);
|
|
|
|
|
|
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdfPath));
|
|
|
int startPage = 1;
|
|
|
if (onlyCollectPageNumbers) {
|
|
|
- pageNumberMap.clear();
|
|
|
+ pageNumberMap.clear();
|
|
|
Pattern startPattern = Pattern.compile("^1\\.\\s+报告概述$");
|
|
|
|
|
|
// 查找起始页
|
|
@@ -597,9 +582,8 @@ public class OfficeUtil {
|
|
|
|
|
|
// 收集标题和页码
|
|
|
Pattern titlePattern = Pattern.compile(
|
|
|
- "^((\\d+)\\.\\s+|(\\d+\\.\\d+)\\s+)([\\u4e00-\\u9fa5a-zA-Z0-9].*)$",
|
|
|
- Pattern.MULTILINE
|
|
|
- );
|
|
|
+ "^((\\d+)\\.\\s+|(\\d+\\.\\d+)\\s+)([\\u4e00-\\u9fa5a-zA-Z0-9].*)$",
|
|
|
+ Pattern.MULTILINE);
|
|
|
Pattern specialPattern = Pattern.compile("^重要声明\\s*[::]?\\s*(.*)$");
|
|
|
|
|
|
for (int pageNum = startPage; pageNum <= reader.getNumberOfPages(); pageNum++) {
|
|
@@ -610,11 +594,11 @@ public class OfficeUtil {
|
|
|
if (line.isEmpty()) {
|
|
|
continue;
|
|
|
}
|
|
|
- if (line.startsWith("6.3 ISO体系认证证书或行业经营许可证")) {
|
|
|
+ if (line.contains(".3 ISO体系认证证书或行业经营许可证")) {
|
|
|
String nextLine = (i + 1 < lines.length) ? lines[i + 1].trim() : "";
|
|
|
line = line + (nextLine.isEmpty() ? "" : nextLine);
|
|
|
}
|
|
|
- if (line.startsWith("7.8 机构管理层(法定代表人,理事长,秘书长)")) {
|
|
|
+ if (line.contains(".8 机构管理层(法定代表人,理事长,秘书长)")) {
|
|
|
String nextLine = (i + 1 < lines.length) ? lines[i + 1].trim() : "";
|
|
|
line = line + (nextLine.isEmpty() ? "" : nextLine);
|
|
|
}
|
|
@@ -633,7 +617,7 @@ public class OfficeUtil {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- //一级标题图形背景
|
|
|
+ // 一级标题图形背景
|
|
|
Pattern firstLevelTitlePattern = Pattern.compile("^(\\d+)\\.\\s+([\\u4e00-\\u9fa5a-zA-Z].*)$");
|
|
|
Set<Integer> styledPages = new HashSet<>();
|
|
|
startPage = pageNumberMap.get("startPage");
|
|
@@ -662,9 +646,9 @@ public class OfficeUtil {
|
|
|
underContent.saveState();
|
|
|
underContent.setColorFill(new Color(210, 235, 255)); // 浅蓝色填充
|
|
|
underContent.circle(
|
|
|
- xPos,
|
|
|
- yPos - 8, // 圆形中心Y
|
|
|
- 10 // 半径
|
|
|
+ xPos,
|
|
|
+ yPos - 8, // 圆形中心Y
|
|
|
+ 10 // 半径
|
|
|
);
|
|
|
underContent.fill();
|
|
|
underContent.restoreState();
|
|
@@ -680,7 +664,7 @@ public class OfficeUtil {
|
|
|
|
|
|
}
|
|
|
|
|
|
- //封面背景
|
|
|
+ // 封面背景
|
|
|
PdfContentByte background = stamper.getUnderContent(1);
|
|
|
Image image = Image.getInstance(backgroundImagePath);
|
|
|
image.scaleAbsolute(PageSize.A4.getWidth(), PageSize.A4.getHeight());
|
|
@@ -690,22 +674,4 @@ public class OfficeUtil {
|
|
|
stamper.close();
|
|
|
reader.close();
|
|
|
}
|
|
|
-
|
|
|
-// private static boolean isTableNearBottom(PdfWriter writer, PdfPTable table, float bottom) {
|
|
|
-// try {
|
|
|
-// // 获取当前页面的剩余高度
|
|
|
-// float remainingHeight = writer.getVerticalPosition(true) - bottom;
|
|
|
-//
|
|
|
-// // 估算当前行高度
|
|
|
-// float estimatedRowHeight = 30f;
|
|
|
-// float estimatedTableHeight = table.getRows().size() * estimatedRowHeight;
|
|
|
-//
|
|
|
-// // 如果剩余空间不足以容纳整个表格,则换页
|
|
|
-// return remainingHeight < estimatedTableHeight;
|
|
|
-// } catch (Exception e) {
|
|
|
-// e.printStackTrace();
|
|
|
-// return false;
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
}
|