|
|
@@ -1,22 +1,18 @@
|
|
|
package com.hotent.project.service;
|
|
|
|
|
|
-import org.docx4j.Docx4J;
|
|
|
-import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
|
|
|
-import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
|
|
|
-import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
|
|
|
-import org.docx4j.relationships.Relationship;
|
|
|
-import org.docx4j.wml.*;
|
|
|
-
|
|
|
-import javax.xml.bind.JAXBElement;
|
|
|
+import com.aspose.words.*;
|
|
|
+import com.aspose.cells.Workbook;
|
|
|
+import com.aspose.cells.PdfSaveOptions;
|
|
|
+
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
import java.io.File;
|
|
|
-import java.io.FileInputStream;
|
|
|
-import java.math.BigInteger;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
|
|
|
/**
|
|
|
- * 卷宗生成测试类(使用docx4j实现节分隔符控制页码)
|
|
|
- * 测试封面、目录、正文、封底的合并效果
|
|
|
+ * 卷宗生成测试类(使用Aspose实现多格式合并)
|
|
|
+ * 支持:Word(.docx/.doc)、Excel(.xlsx/.xls)、PDF(.pdf)
|
|
|
*
|
|
|
* 页码规则:
|
|
|
* - 封面:无页码
|
|
|
@@ -26,10 +22,8 @@ import java.util.List;
|
|
|
*/
|
|
|
public class ArchiveTest {
|
|
|
|
|
|
- private static ObjectFactory factory = new ObjectFactory();
|
|
|
-
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
- System.out.println("========== 卷宗生成测试(docx4j版)==========\n");
|
|
|
+ System.out.println("========== 卷宗生成测试(Aspose版)==========\n");
|
|
|
|
|
|
// 测试文件路径
|
|
|
String basePath = "D:\\fx\\cc\\";
|
|
|
@@ -42,28 +36,21 @@ public class ArchiveTest {
|
|
|
List<String> catalogFiles = new ArrayList<>();
|
|
|
catalogFiles.add(basePath + "卷内目录20251225155255833.docx");
|
|
|
|
|
|
- // 正文文件(14个资料类别)
|
|
|
+ // 正文文件(支持Word、Excel、PDF混合)
|
|
|
List<String> contentFiles = new ArrayList<>();
|
|
|
- contentFiles.add(basePath + "政府定价成本监审结论报告20251225151207150.docx");
|
|
|
- contentFiles.add(basePath + "成本监审通知书20251225142953160.docx");
|
|
|
- contentFiles.add(basePath + "成本监审通知书20251225143004173.docx");
|
|
|
+ // Excel文件
|
|
|
+ contentFiles.add(basePath + "小店区-幼儿教育成本监审表(封面)_176664431290620251225143152602.xlsx");
|
|
|
+ contentFiles.add(basePath + "小店区-幼儿教育成本监审表(固定资产) _176664457050920251225143610719.xlsx");
|
|
|
+ // 更多Word文件
|
|
|
contentFiles.add(basePath + "成本监审提取资料登记表20251225154211987.docx");
|
|
|
contentFiles.add(basePath + "报送资料文件20251225143050824.docx");
|
|
|
- contentFiles.add(basePath + "报送资料文件20251225143055127.docx");
|
|
|
- contentFiles.add(basePath + "成本监审补充资料通知书20251225150624580.docx");
|
|
|
- contentFiles.add(basePath + "成本审核初步意见告知书20251225150642481.docx");
|
|
|
- contentFiles.add(basePath + "成本审核初步意见表20251225153700986.docx");
|
|
|
- contentFiles.add(basePath + "成本监审集体审议记录122520251225154018971.docx");
|
|
|
- contentFiles.add(basePath + "成本监审工作底稿20251225153205828.docx");
|
|
|
- contentFiles.add(basePath + "中止定价成本监审通知书20251225150552396.docx");
|
|
|
- contentFiles.add(basePath + "中止定价成本监审通知书20251225150608304.docx");
|
|
|
|
|
|
// 封底文件
|
|
|
List<String> backCoverFiles = new ArrayList<>();
|
|
|
backCoverFiles.add(basePath + "案卷封底20251225155301766.docx");
|
|
|
|
|
|
// 输出路径
|
|
|
- String outputPath = basePath + "卷宗_docx4j_" + System.currentTimeMillis() + ".docx";
|
|
|
+ String outputPath = basePath + "卷宗_Aspose_" + System.currentTimeMillis() + ".docx";
|
|
|
|
|
|
// 合并文档
|
|
|
mergeDocuments(coverFiles, catalogFiles, contentFiles, backCoverFiles, outputPath);
|
|
|
@@ -73,7 +60,7 @@ public class ArchiveTest {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 合并多个Word文档,支持分节控制页码
|
|
|
+ * 合并多个文档(支持Word、Excel、PDF)
|
|
|
*
|
|
|
* @param coverFiles 封面文件列表
|
|
|
* @param catalogFiles 目录文件列表
|
|
|
@@ -82,14 +69,13 @@ public class ArchiveTest {
|
|
|
* @param outputPath 输出路径
|
|
|
*/
|
|
|
public static void mergeDocuments(List<String> coverFiles, List<String> catalogFiles,
|
|
|
- List<String> contentFiles, List<String> backCoverFiles,
|
|
|
- String outputPath) throws Exception {
|
|
|
- System.out.println("开始使用docx4j合并文档...");
|
|
|
+ List<String> contentFiles, List<String> backCoverFiles,
|
|
|
+ String outputPath) throws Exception {
|
|
|
+ System.out.println("开始使用Aspose合并文档...");
|
|
|
|
|
|
- // 创建主文档
|
|
|
- WordprocessingMLPackage mainDoc = WordprocessingMLPackage.createPackage();
|
|
|
- MainDocumentPart mainPart = mainDoc.getMainDocumentPart();
|
|
|
- Body body = mainPart.getContents().getBody();
|
|
|
+ // 创建主文档(不使用ensureMinimum,避免产生空白页)
|
|
|
+ Document mainDoc = new Document();
|
|
|
+ mainDoc.removeAllChildren();
|
|
|
|
|
|
boolean isFirst = true;
|
|
|
|
|
|
@@ -99,11 +85,9 @@ public class ArchiveTest {
|
|
|
for (String filePath : coverFiles) {
|
|
|
if (filePath != null && new File(filePath).exists()) {
|
|
|
System.out.println(" ✓ " + extractFileName(filePath));
|
|
|
- isFirst = mergeWordFile(mainDoc, body, filePath, isFirst);
|
|
|
+ isFirst = appendDocument(mainDoc, filePath, isFirst, SectionStart.NEW_PAGE);
|
|
|
}
|
|
|
}
|
|
|
- // 封面后添加节分隔符(无页脚)
|
|
|
- addSectionBreak(body, false, null);
|
|
|
}
|
|
|
|
|
|
// 2. 合并目录(无页码节)
|
|
|
@@ -112,303 +96,385 @@ public class ArchiveTest {
|
|
|
for (String filePath : catalogFiles) {
|
|
|
if (filePath != null && new File(filePath).exists()) {
|
|
|
System.out.println(" ✓ " + extractFileName(filePath));
|
|
|
- isFirst = mergeWordFile(mainDoc, body, filePath, isFirst);
|
|
|
+ isFirst = appendDocument(mainDoc, filePath, isFirst, SectionStart.NEW_PAGE);
|
|
|
}
|
|
|
}
|
|
|
- // 目录后添加节分隔符(准备进入有页码的正文)
|
|
|
- addSectionBreak(body, false, null);
|
|
|
}
|
|
|
|
|
|
- // 3. 创建正文的页脚(有页码)
|
|
|
- FooterPart footerPart = null;
|
|
|
- Relationship footerRel = null;
|
|
|
- if (contentFiles != null && !contentFiles.isEmpty()) {
|
|
|
- footerPart = createFooterWithPageNumber(mainDoc);
|
|
|
- footerRel = mainDoc.getMainDocumentPart().addTargetPart(footerPart);
|
|
|
- }
|
|
|
+ // 记录正文开始的节索引
|
|
|
+ int contentStartSectionIndex = mainDoc.getSections().getCount();
|
|
|
|
|
|
- // 4. 合并正文(有页码节)
|
|
|
+ // 3. 合并正文(有页码节)
|
|
|
if (contentFiles != null && !contentFiles.isEmpty()) {
|
|
|
System.out.println("\n3. 合并正文(有页码)...");
|
|
|
for (String filePath : contentFiles) {
|
|
|
if (filePath != null && new File(filePath).exists()) {
|
|
|
System.out.println(" ✓ " + extractFileName(filePath));
|
|
|
- isFirst = mergeWordFile(mainDoc, body, filePath, isFirst);
|
|
|
+ isFirst = appendDocument(mainDoc, filePath, isFirst, SectionStart.NEW_PAGE);
|
|
|
}
|
|
|
}
|
|
|
- // 正文后添加节分隔符(带页脚引用,然后进入无页码的封底)
|
|
|
- if (backCoverFiles != null && !backCoverFiles.isEmpty()) {
|
|
|
- addSectionBreak(body, true, footerRel);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- // 5. 合并封底(无页码节)
|
|
|
+ // 记录正文结束的节索引
|
|
|
+ int contentEndSectionIndex = mainDoc.getSections().getCount() - 1;
|
|
|
+
|
|
|
+ // 4. 合并封底(无页码节)
|
|
|
if (backCoverFiles != null && !backCoverFiles.isEmpty()) {
|
|
|
System.out.println("\n4. 合并封底...");
|
|
|
for (String filePath : backCoverFiles) {
|
|
|
if (filePath != null && new File(filePath).exists()) {
|
|
|
System.out.println(" ✓ " + extractFileName(filePath));
|
|
|
- isFirst = mergeWordFile(mainDoc, body, filePath, isFirst);
|
|
|
+ isFirst = appendDocument(mainDoc, filePath, isFirst, SectionStart.NEW_PAGE);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 设置文档最后一节的属性(封底无页脚)
|
|
|
- setLastSectionProperties(body, false, null);
|
|
|
+ // 5. 计算正文总页数并设置页码
|
|
|
+ System.out.println("\n5. 设置页码...");
|
|
|
+ int contentTotalPages = calculateContentPages(mainDoc, contentStartSectionIndex, contentEndSectionIndex);
|
|
|
+ System.out.println(" 正文总页数: " + contentTotalPages);
|
|
|
+ setupPageNumbers(mainDoc, contentStartSectionIndex, contentEndSectionIndex, contentTotalPages);
|
|
|
|
|
|
// 保存文档
|
|
|
File outputFile = new File(outputPath);
|
|
|
- if (!outputFile.getParentFile().exists()) {
|
|
|
+ if (outputFile.getParentFile() != null && !outputFile.getParentFile().exists()) {
|
|
|
outputFile.getParentFile().mkdirs();
|
|
|
}
|
|
|
- Docx4J.save(mainDoc, outputFile);
|
|
|
+ mainDoc.save(outputPath);
|
|
|
System.out.println("\n文档保存成功:" + outputPath);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 合并单个Word文件
|
|
|
+ * 计算正文部分的总页数
|
|
|
*/
|
|
|
- private static boolean mergeWordFile(WordprocessingMLPackage mainDoc, Body body,
|
|
|
- String filePath, boolean isFirst) throws Exception {
|
|
|
- File file = new File(filePath);
|
|
|
- if (!file.exists()) {
|
|
|
- System.out.println(" [警告] 文件不存在:" + filePath);
|
|
|
- return isFirst;
|
|
|
- }
|
|
|
+ private static int calculateContentPages(Document doc, int contentStartSectionIndex, int contentEndSectionIndex) throws Exception {
|
|
|
+ // 更新文档布局以获取准确的页数
|
|
|
+ doc.updatePageLayout();
|
|
|
|
|
|
- String lowerPath = filePath.toLowerCase();
|
|
|
- if (!lowerPath.endsWith(".docx") && !lowerPath.endsWith(".doc")) {
|
|
|
- System.out.println(" [警告] 不支持的文件格式:" + filePath);
|
|
|
- return isFirst;
|
|
|
- }
|
|
|
+ int totalPages = 0;
|
|
|
+ SectionCollection sections = doc.getSections();
|
|
|
|
|
|
- // 添加分页符(非第一个文档)
|
|
|
- if (!isFirst) {
|
|
|
- addPageBreak(body);
|
|
|
+ for (int i = contentStartSectionIndex; i <= contentEndSectionIndex && i < sections.getCount(); i++) {
|
|
|
+ Section section = sections.get(i);
|
|
|
+ // 获取该节的页数
|
|
|
+ int sectionPages = doc.getPageCount(); // 这是整个文档的页数,需要另外计算
|
|
|
}
|
|
|
|
|
|
- // 加载源文档
|
|
|
- WordprocessingMLPackage sourceDoc = WordprocessingMLPackage.load(new FileInputStream(file));
|
|
|
- List<Object> sourceContent = sourceDoc.getMainDocumentPart().getContent();
|
|
|
-
|
|
|
- // 复制内容到主文档
|
|
|
- for (Object obj : sourceContent) {
|
|
|
- // 跳过节属性(SectPr),避免干扰
|
|
|
- if (obj instanceof JAXBElement) {
|
|
|
- JAXBElement<?> element = (JAXBElement<?>) obj;
|
|
|
- if (element.getValue() instanceof SectPr) {
|
|
|
- continue;
|
|
|
+ // 使用LayoutCollector获取精确页数
|
|
|
+ LayoutCollector collector = new LayoutCollector(doc);
|
|
|
+ int contentStartPage = Integer.MAX_VALUE;
|
|
|
+ int contentEndPage = 0;
|
|
|
+
|
|
|
+ for (int i = contentStartSectionIndex; i <= contentEndSectionIndex && i < sections.getCount(); i++) {
|
|
|
+ Section section = sections.get(i);
|
|
|
+ // 获取节的第一个段落所在页
|
|
|
+ Node firstNode = section.getBody().getFirstChild();
|
|
|
+ Node lastNode = section.getBody().getLastChild();
|
|
|
+
|
|
|
+ if (firstNode != null) {
|
|
|
+ int startPage = collector.getStartPageIndex(firstNode);
|
|
|
+ if (startPage > 0 && startPage < contentStartPage) {
|
|
|
+ contentStartPage = startPage;
|
|
|
}
|
|
|
}
|
|
|
- if (obj instanceof SectPr) {
|
|
|
- continue;
|
|
|
+ if (lastNode != null) {
|
|
|
+ int endPage = collector.getEndPageIndex(lastNode);
|
|
|
+ if (endPage > contentEndPage) {
|
|
|
+ contentEndPage = endPage;
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- // 深拷贝并添加到主文档
|
|
|
- Object cloned = org.docx4j.XmlUtils.deepCopy(obj);
|
|
|
- body.getContent().add(cloned);
|
|
|
+ if (contentStartPage != Integer.MAX_VALUE && contentEndPage > 0) {
|
|
|
+ totalPages = contentEndPage - contentStartPage + 1;
|
|
|
}
|
|
|
|
|
|
- return false;
|
|
|
+ return totalPages;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 添加分页符
|
|
|
+ * 追加文档(自动识别格式)
|
|
|
*/
|
|
|
- private static void addPageBreak(Body body) {
|
|
|
- P p = factory.createP();
|
|
|
- R r = factory.createR();
|
|
|
- Br br = factory.createBr();
|
|
|
- br.setType(STBrType.PAGE);
|
|
|
- r.getContent().add(br);
|
|
|
- p.getContent().add(r);
|
|
|
- body.getContent().add(p);
|
|
|
+ private static boolean appendDocument(Document mainDoc, String filePath, boolean isFirst, int sectionStart) throws Exception {
|
|
|
+ String lowerPath = filePath.toLowerCase();
|
|
|
+ Document docToAppend;
|
|
|
+
|
|
|
+ if (lowerPath.endsWith(".docx") || lowerPath.endsWith(".doc")) {
|
|
|
+ // Word文档直接加载
|
|
|
+ docToAppend = new Document(filePath);
|
|
|
+ } else if (lowerPath.endsWith(".xlsx") || lowerPath.endsWith(".xls")) {
|
|
|
+ // Excel转Word
|
|
|
+ docToAppend = convertExcelToWord(filePath);
|
|
|
+ } else if (lowerPath.endsWith(".pdf")) {
|
|
|
+ // PDF转Word
|
|
|
+ docToAppend = convertPdfToWord(filePath);
|
|
|
+ } else {
|
|
|
+ System.out.println(" [警告] 不支持的文件格式:" + filePath);
|
|
|
+ return isFirst;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (docToAppend != null && docToAppend.getSections().getCount() > 0) {
|
|
|
+ if (!isFirst) {
|
|
|
+ // 设置第一节的分节符类型
|
|
|
+ docToAppend.getFirstSection().getPageSetup().setSectionStart(sectionStart);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 追加到主文档
|
|
|
+ mainDoc.appendDocument(docToAppend, ImportFormatMode.KEEP_SOURCE_FORMATTING);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return isFirst;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 添加节分隔符
|
|
|
- *
|
|
|
- * @param body 文档Body
|
|
|
- * @param withFooter 是否带页脚
|
|
|
- * @param footerRel 页脚关系(如果withFooter为true)
|
|
|
+ * Excel转Word文档
|
|
|
*/
|
|
|
- private static void addSectionBreak(Body body, boolean withFooter, Relationship footerRel) {
|
|
|
- P p = factory.createP();
|
|
|
- PPr pPr = factory.createPPr();
|
|
|
- SectPr sectPr = factory.createSectPr();
|
|
|
-
|
|
|
- // 设置节类型为下一页
|
|
|
- SectPr.Type type = factory.createSectPrType();
|
|
|
- type.setVal("nextPage");
|
|
|
- sectPr.setType(type);
|
|
|
-
|
|
|
- // 如果需要页脚,添加页脚引用
|
|
|
- if (withFooter && footerRel != null) {
|
|
|
- FooterReference footerRef = factory.createFooterReference();
|
|
|
- footerRef.setType(HdrFtrRef.DEFAULT);
|
|
|
- footerRef.setId(footerRel.getId());
|
|
|
- sectPr.getEGHdrFtrReferences().add(footerRef);
|
|
|
-
|
|
|
- // 页码从1开始
|
|
|
- CTPageNumber pgNumType = factory.createCTPageNumber();
|
|
|
- pgNumType.setStart(BigInteger.valueOf(1));
|
|
|
- sectPr.setPgNumType(pgNumType);
|
|
|
+ private static Document convertExcelToWord(String excelPath) throws Exception {
|
|
|
+ // 加载Excel
|
|
|
+ Workbook workbook = new Workbook(excelPath);
|
|
|
+
|
|
|
+ // 获取第一个工作表
|
|
|
+ com.aspose.cells.Worksheet worksheet = workbook.getWorksheets().get(0);
|
|
|
+ com.aspose.cells.Cells cells = worksheet.getCells();
|
|
|
+
|
|
|
+ // 获取实际使用的行数
|
|
|
+ int maxRow = cells.getMaxDataRow() + 1; // getMaxDataRow返回的是索引,+1得到行数
|
|
|
+
|
|
|
+ // 如果只有三行或更少,转换为普通文本格式
|
|
|
+ if (maxRow <= 3) {
|
|
|
+ return convertExcelToText(workbook);
|
|
|
}
|
|
|
|
|
|
- pPr.setSectPr(sectPr);
|
|
|
- p.setPPr(pPr);
|
|
|
- body.getContent().add(p);
|
|
|
+ // 先转为PDF(保持格式)
|
|
|
+ ByteArrayOutputStream pdfStream = new ByteArrayOutputStream();
|
|
|
+ com.aspose.cells.PdfSaveOptions pdfOptions = new com.aspose.cells.PdfSaveOptions();
|
|
|
+ pdfOptions.setOnePagePerSheet(false); // 允许多页
|
|
|
+ pdfOptions.setAllColumnsInOnePagePerSheet(true); // 所有列在一页
|
|
|
+ workbook.save(pdfStream, pdfOptions);
|
|
|
+
|
|
|
+ // PDF转Word
|
|
|
+ ByteArrayInputStream pdfInput = new ByteArrayInputStream(pdfStream.toByteArray());
|
|
|
+ com.aspose.pdf.Document pdfDoc = new com.aspose.pdf.Document(pdfInput);
|
|
|
+
|
|
|
+ ByteArrayOutputStream wordStream = new ByteArrayOutputStream();
|
|
|
+ pdfDoc.save(wordStream, com.aspose.pdf.SaveFormat.DocX);
|
|
|
+
|
|
|
+ // 加载Word文档
|
|
|
+ ByteArrayInputStream wordInput = new ByteArrayInputStream(wordStream.toByteArray());
|
|
|
+ Document doc = new Document(wordInput);
|
|
|
+
|
|
|
+ return doc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 设置文档最后一节的属性
|
|
|
+ * Excel转为普通文本格式的Word文档(适用于只有两三行的情况)
|
|
|
+ * 第一行为标题,第二行为表头,第三行为数据
|
|
|
+ * 展示格式:表头: 数据
|
|
|
*/
|
|
|
- private static void setLastSectionProperties(Body body, boolean withFooter, Relationship footerRel) {
|
|
|
- SectPr sectPr = body.getSectPr();
|
|
|
- if (sectPr == null) {
|
|
|
- sectPr = factory.createSectPr();
|
|
|
- body.setSectPr(sectPr);
|
|
|
+ private static Document convertExcelToText(Workbook workbook) throws Exception {
|
|
|
+ Document doc = new Document();
|
|
|
+ DocumentBuilder builder = new DocumentBuilder(doc);
|
|
|
+
|
|
|
+ // 获取第一个工作表
|
|
|
+ com.aspose.cells.Worksheet worksheet = workbook.getWorksheets().get(0);
|
|
|
+ com.aspose.cells.Cells cells = worksheet.getCells();
|
|
|
+
|
|
|
+ int maxRow = cells.getMaxDataRow() + 1;
|
|
|
+ int maxCol = cells.getMaxDataColumn() + 1;
|
|
|
+
|
|
|
+ // 第一行是标题,单独输出(居中、加粗、大字号)
|
|
|
+ if (maxRow >= 1) {
|
|
|
+ for (int col = 0; col < maxCol; col++) {
|
|
|
+ com.aspose.cells.Cell cell = cells.get(0, col);
|
|
|
+ String value = cell.getStringValue();
|
|
|
+ if (value != null && !value.trim().isEmpty()) {
|
|
|
+ builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
|
|
|
+ builder.getParagraphFormat().setSpaceAfter(12);
|
|
|
+ builder.getFont().setName("宋体");
|
|
|
+ builder.getFont().setSize(16);
|
|
|
+ builder.getFont().setBold(true);
|
|
|
+ builder.writeln(value.trim());
|
|
|
+ builder.getFont().setBold(false);
|
|
|
+ break; // 标题通常只有一个
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 清除现有页脚引用
|
|
|
- sectPr.getEGHdrFtrReferences().clear();
|
|
|
+ // 第二行是表头,第三行是数据,配对输出
|
|
|
+ if (maxRow >= 3) {
|
|
|
+ // 重置段落格式
|
|
|
+ builder.getParagraphFormat().setAlignment(ParagraphAlignment.LEFT);
|
|
|
+ builder.getParagraphFormat().setSpaceAfter(6);
|
|
|
+ builder.getParagraphFormat().setFirstLineIndent(24); // 首行缩进
|
|
|
+ builder.getFont().setSize(12);
|
|
|
+
|
|
|
+ for (int col = 0; col < maxCol; col++) {
|
|
|
+ com.aspose.cells.Cell headerCell = cells.get(1, col); // 表头
|
|
|
+ com.aspose.cells.Cell dataCell = cells.get(2, col); // 数据
|
|
|
|
|
|
- if (withFooter && footerRel != null) {
|
|
|
- FooterReference footerRef = factory.createFooterReference();
|
|
|
- footerRef.setType(HdrFtrRef.DEFAULT);
|
|
|
- footerRef.setId(footerRel.getId());
|
|
|
- sectPr.getEGHdrFtrReferences().add(footerRef);
|
|
|
+ String header = headerCell.getStringValue();
|
|
|
+ String data = dataCell.getStringValue();
|
|
|
+
|
|
|
+ if (header != null && !header.trim().isEmpty()) {
|
|
|
+ String line = header.trim() + ":" + (data != null ? data.trim() : "");
|
|
|
+ builder.writeln(line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (maxRow == 2) {
|
|
|
+ // 只有两行:第一行标题,第二行数据
|
|
|
+ builder.getParagraphFormat().setAlignment(ParagraphAlignment.LEFT);
|
|
|
+ builder.getParagraphFormat().setSpaceAfter(6);
|
|
|
+ builder.getFont().setSize(12);
|
|
|
+
|
|
|
+ for (int col = 0; col < maxCol; col++) {
|
|
|
+ com.aspose.cells.Cell cell = cells.get(1, col);
|
|
|
+ String value = cell.getStringValue();
|
|
|
+ if (value != null && !value.trim().isEmpty()) {
|
|
|
+ builder.writeln(value.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- /**
|
|
|
- * 创建带页码的页脚
|
|
|
- * 格式:第 X 页 / 共 Y 页
|
|
|
- */
|
|
|
- private static FooterPart createFooterWithPageNumber(WordprocessingMLPackage wordPackage) throws Exception {
|
|
|
- FooterPart footerPart = new FooterPart();
|
|
|
- footerPart.setPackage(wordPackage);
|
|
|
-
|
|
|
- Ftr footer = factory.createFtr();
|
|
|
-
|
|
|
- // 创建段落
|
|
|
- P p = factory.createP();
|
|
|
-
|
|
|
- // 设置段落居中
|
|
|
- PPr pPr = factory.createPPr();
|
|
|
- Jc jc = factory.createJc();
|
|
|
- jc.setVal(JcEnumeration.CENTER);
|
|
|
- pPr.setJc(jc);
|
|
|
- p.setPPr(pPr);
|
|
|
-
|
|
|
- // 创建字体属性
|
|
|
- RPr rPr = createFontRPr("宋体", "20");
|
|
|
-
|
|
|
- // "第 "
|
|
|
- R r1 = factory.createR();
|
|
|
- r1.setRPr(rPr);
|
|
|
- Text t1 = factory.createText();
|
|
|
- t1.setValue("第 ");
|
|
|
- t1.setSpace("preserve");
|
|
|
- r1.getContent().add(t1);
|
|
|
- p.getContent().add(r1);
|
|
|
-
|
|
|
- // PAGE 字段(当前页码)
|
|
|
- addFieldToRun(p, "PAGE", rPr);
|
|
|
-
|
|
|
- // " 页 / 共 "
|
|
|
- R r2 = factory.createR();
|
|
|
- r2.setRPr(rPr);
|
|
|
- Text t2 = factory.createText();
|
|
|
- t2.setValue(" 页 / 共 ");
|
|
|
- t2.setSpace("preserve");
|
|
|
- r2.getContent().add(t2);
|
|
|
- p.getContent().add(r2);
|
|
|
-
|
|
|
- // NUMPAGES 字段(总页数)
|
|
|
- addFieldToRun(p, "NUMPAGES", rPr);
|
|
|
-
|
|
|
- // " 页"
|
|
|
- R r3 = factory.createR();
|
|
|
- r3.setRPr(rPr);
|
|
|
- Text t3 = factory.createText();
|
|
|
- t3.setValue(" 页");
|
|
|
- r3.getContent().add(t3);
|
|
|
- p.getContent().add(r3);
|
|
|
-
|
|
|
- footer.getContent().add(p);
|
|
|
- footerPart.setContents(footer);
|
|
|
-
|
|
|
- return footerPart;
|
|
|
+ return doc;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
- * 添加字段到段落
|
|
|
+ * PDF转Word文档
|
|
|
*/
|
|
|
- private static void addFieldToRun(P p, String fieldName, RPr rPr) {
|
|
|
- // 字段开始
|
|
|
- R rBegin = factory.createR();
|
|
|
- rBegin.setRPr(rPr);
|
|
|
- FldChar fldCharBegin = factory.createFldChar();
|
|
|
- fldCharBegin.setFldCharType(STFldCharType.BEGIN);
|
|
|
- rBegin.getContent().add(fldCharBegin);
|
|
|
- p.getContent().add(rBegin);
|
|
|
-
|
|
|
- // 字段指令
|
|
|
- R rInstr = factory.createR();
|
|
|
- rInstr.setRPr(rPr);
|
|
|
- Text instrText = factory.createText();
|
|
|
- instrText.setValue(" " + fieldName + " ");
|
|
|
- instrText.setSpace("preserve");
|
|
|
- JAXBElement<Text> instrTextElement = factory.createRInstrText(instrText);
|
|
|
- rInstr.getContent().add(instrTextElement);
|
|
|
- p.getContent().add(rInstr);
|
|
|
-
|
|
|
- // 字段分隔
|
|
|
- R rSep = factory.createR();
|
|
|
- rSep.setRPr(rPr);
|
|
|
- FldChar fldCharSep = factory.createFldChar();
|
|
|
- fldCharSep.setFldCharType(STFldCharType.SEPARATE);
|
|
|
- rSep.getContent().add(fldCharSep);
|
|
|
- p.getContent().add(rSep);
|
|
|
-
|
|
|
- // 默认值
|
|
|
- R rDefault = factory.createR();
|
|
|
- rDefault.setRPr(rPr);
|
|
|
- Text defaultText = factory.createText();
|
|
|
- defaultText.setValue("1");
|
|
|
- rDefault.getContent().add(defaultText);
|
|
|
- p.getContent().add(rDefault);
|
|
|
-
|
|
|
- // 字段结束
|
|
|
- R rEnd = factory.createR();
|
|
|
- rEnd.setRPr(rPr);
|
|
|
- FldChar fldCharEnd = factory.createFldChar();
|
|
|
- fldCharEnd.setFldCharType(STFldCharType.END);
|
|
|
- rEnd.getContent().add(fldCharEnd);
|
|
|
- p.getContent().add(rEnd);
|
|
|
+ private static Document convertPdfToWord(String pdfPath) throws Exception {
|
|
|
+ // 加载PDF
|
|
|
+ com.aspose.pdf.Document pdfDoc = new com.aspose.pdf.Document(pdfPath);
|
|
|
+
|
|
|
+ // 转为Word
|
|
|
+ ByteArrayOutputStream wordStream = new ByteArrayOutputStream();
|
|
|
+ pdfDoc.save(wordStream, com.aspose.pdf.SaveFormat.DocX);
|
|
|
+
|
|
|
+ // 加载Word文档
|
|
|
+ ByteArrayInputStream wordInput = new ByteArrayInputStream(wordStream.toByteArray());
|
|
|
+ return new Document(wordInput);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 创建字体属性
|
|
|
+ * 设置页码(只有正文部分显示页码)
|
|
|
+ *
|
|
|
+ * @param doc 文档
|
|
|
+ * @param contentStartSectionIndex 正文开始的节索引
|
|
|
+ * @param contentEndSectionIndex 正文结束的节索引
|
|
|
+ * @param contentTotalPages 正文总页数
|
|
|
*/
|
|
|
- private static RPr createFontRPr(String fontName, String fontSize) {
|
|
|
- RPr rPr = factory.createRPr();
|
|
|
-
|
|
|
- // 字体
|
|
|
- RFonts rFonts = factory.createRFonts();
|
|
|
- rFonts.setAscii(fontName);
|
|
|
- rFonts.setEastAsia(fontName);
|
|
|
- rFonts.setHAnsi(fontName);
|
|
|
- rPr.setRFonts(rFonts);
|
|
|
-
|
|
|
- // 字号
|
|
|
- HpsMeasure sz = factory.createHpsMeasure();
|
|
|
- sz.setVal(new BigInteger(fontSize));
|
|
|
- rPr.setSz(sz);
|
|
|
- rPr.setSzCs(sz);
|
|
|
-
|
|
|
- return rPr;
|
|
|
+ private static void setupPageNumbers(Document doc, int contentStartSectionIndex, int contentEndSectionIndex, int contentTotalPages) throws Exception {
|
|
|
+ SectionCollection sections = doc.getSections();
|
|
|
+
|
|
|
+ for (int i = 0; i < sections.getCount(); i++) {
|
|
|
+ Section section = sections.get(i);
|
|
|
+ HeaderFooterCollection headerFooters = section.getHeadersFooters();
|
|
|
+
|
|
|
+ // 取消链接到上一节(使每节页眉页脚独立)
|
|
|
+ headerFooters.linkToPrevious(false);
|
|
|
+
|
|
|
+ // 清除现有页脚
|
|
|
+ HeaderFooter existingFooter = headerFooters.getByHeaderFooterType(HeaderFooterType.FOOTER_PRIMARY);
|
|
|
+ if (existingFooter != null) {
|
|
|
+ existingFooter.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清除现有页眉
|
|
|
+ HeaderFooter existingHeader = headerFooters.getByHeaderFooterType(HeaderFooterType.HEADER_PRIMARY);
|
|
|
+ if (existingHeader != null) {
|
|
|
+ existingHeader.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只有正文部分添加页码和页眉编号
|
|
|
+ if (i >= contentStartSectionIndex && i <= contentEndSectionIndex) {
|
|
|
+ // 页码设置:只有正文第一节从1开始,后续节继续累加
|
|
|
+ if (i == contentStartSectionIndex) {
|
|
|
+ // 正文第一节:页码从1开始
|
|
|
+ section.getPageSetup().setRestartPageNumbering(true);
|
|
|
+ section.getPageSetup().setPageStartingNumber(1);
|
|
|
+ } else {
|
|
|
+ // 正文后续节:页码继续累加,不重新开始
|
|
|
+ section.getPageSetup().setRestartPageNumbering(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 创建页眉(右上角编号 001、002、003...)==========
|
|
|
+ HeaderFooter header = new HeaderFooter(doc, HeaderFooterType.HEADER_PRIMARY);
|
|
|
+ headerFooters.add(header);
|
|
|
+
|
|
|
+ // 创建页眉段落(右对齐)
|
|
|
+ Paragraph headerPara = new Paragraph(doc);
|
|
|
+ headerPara.getParagraphFormat().setAlignment(ParagraphAlignment.RIGHT);
|
|
|
+ // 设置右缩进,增加与右侧边距的距离
|
|
|
+ headerPara.getParagraphFormat().setRightIndent(20);
|
|
|
+ // 去掉页眉下边框线
|
|
|
+ headerPara.getParagraphFormat().getBorders().getBottom().setLineStyle(LineStyle.NONE);
|
|
|
+
|
|
|
+ // 先将段落添加到header中
|
|
|
+ header.appendChild(headerPara);
|
|
|
+
|
|
|
+ // 使用DocumentBuilder插入带格式的页码字段
|
|
|
+ DocumentBuilder builder = new DocumentBuilder(doc);
|
|
|
+ builder.moveTo(headerPara);
|
|
|
+
|
|
|
+ // 设置字体(加粗)
|
|
|
+ builder.getFont().setName("宋体");
|
|
|
+ builder.getFont().setSize(12);
|
|
|
+ builder.getFont().setBold(true);
|
|
|
+
|
|
|
+ // 插入带格式的PAGE字段,格式为3位数字
|
|
|
+ builder.insertField("PAGE \\# \"000\"", "001");
|
|
|
+
|
|
|
+ // 设置页眉距离
|
|
|
+ section.getPageSetup().setHeaderDistance(20);
|
|
|
+
|
|
|
+ // ========== 创建页脚(第 X 页 / 共 Y 页)==========
|
|
|
+ HeaderFooter footer = new HeaderFooter(doc, HeaderFooterType.FOOTER_PRIMARY);
|
|
|
+ headerFooters.add(footer);
|
|
|
+
|
|
|
+ // 创建页脚段落
|
|
|
+ Paragraph footerPara = new Paragraph(doc);
|
|
|
+ footerPara.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
|
|
|
+ footerPara.getParagraphFormat().setSpaceBefore(12);
|
|
|
+
|
|
|
+ // 设置页脚距离页面底部的距离
|
|
|
+ section.getPageSetup().setFooterDistance(30);
|
|
|
+
|
|
|
+ // 添加页码文本:第 X 页 / 共 Y 页
|
|
|
+ Run run1 = new Run(doc, "第 ");
|
|
|
+ run1.getFont().setName("宋体");
|
|
|
+ run1.getFont().setSize(10.5);
|
|
|
+ footerPara.appendChild(run1);
|
|
|
+
|
|
|
+ // 当前页码字段
|
|
|
+ footerPara.appendField(FieldType.FIELD_PAGE, true);
|
|
|
+
|
|
|
+ Run run2 = new Run(doc, " 页 / 共 ");
|
|
|
+ run2.getFont().setName("宋体");
|
|
|
+ run2.getFont().setSize(10.5);
|
|
|
+ footerPara.appendChild(run2);
|
|
|
+
|
|
|
+ // 使用计算出的正文总页数(固定值)
|
|
|
+ Run runTotal = new Run(doc, String.valueOf(contentTotalPages));
|
|
|
+ runTotal.getFont().setName("宋体");
|
|
|
+ runTotal.getFont().setSize(10.5);
|
|
|
+ footerPara.appendChild(runTotal);
|
|
|
+
|
|
|
+ Run run3 = new Run(doc, " 页");
|
|
|
+ run3.getFont().setName("宋体");
|
|
|
+ run3.getFont().setSize(10.5);
|
|
|
+ footerPara.appendChild(run3);
|
|
|
+
|
|
|
+ footer.appendChild(footerPara);
|
|
|
+ } else {
|
|
|
+ // 封面、目录、封底:不添加页眉页脚,且不重新开始页码
|
|
|
+ section.getPageSetup().setRestartPageNumbering(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新所有字段,使页码格式生效
|
|
|
+ doc.updateFields();
|
|
|
}
|
|
|
|
|
|
/**
|