Java word通過html設置樣式(Spire Docx)
<dependencies> <!-- Apache POI dependency for Word --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.office.free</artifactId> <version>5.3.1</version> </dependency> </dependencies>
<repositories> <repository> <id>com.e-iceblue</id> <name>e-iceblue</name> <url>https://repo.e-iceblue.cn/repository/maven-public/</url> </repository> </repositories>
import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.spire.doc.*; import com.spire.doc.documents.BorderStyle; import com.spire.doc.documents.Paragraph; import com.spire.doc.documents.RowAlignment; import com.spire.doc.fields.DocPicture; import com.spire.doc.formatting.ParagraphFormat; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; @Api(tags = "test") @Slf4j @Controller @RequestMapping("/test") public class HouseSalesController extends BaseController { //resources/templates/word private final String RESOURCE_WORD_URL = FileUtils.getResourceUrl("templates/word") + "/"; private static final String POINT = " • "; private static final String FONT_FAMILY = "Arial";//Malgun Gothic or Gotham or Arial private static final int TEXT_FONT_SIZE = 8;//px private static final int TITLE_FONT_SIZE = 12;//px @PostMapping(value = "/downloadList") public void download(@RequestBody Param param, HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException { //Word path final String templateFilePath = RESOURCE_WORD_URL + "test.docx"; //destination file final String destinationFileName = "test_" + System.currentTimeMillis() + ".docx"; final String destinationFilePath = RESOURCE_WORD_URL + destinationFileName; Map<String, String> maps = Maps.newHashMap(); //searchkey, replaceValue maps.put(HouseSalesFields.TITLE.getValue(), "Viewing Schedule"); maps.put(HouseSalesFields.NAME.getValue(), "Miss Cherry Wong"); maps.put(HouseSalesFields.PRINT_DATE.getValue(), DateUtil.dateToString(new Date(), DateUtil.DATE_FORMAT_EN)); maps.put(HouseSalesFields.AGENT_NAME.getValue(), "Teresa Chan"); maps.put(HouseSalesFields.OFFICE.getValue(), "+852 28424492"); maps.put(HouseSalesFields.MOBILE.getValue(), "+852 94037026"); maps.put(HouseSalesFields.LICENCE_NO.getValue(), "S-269983"); maps.put(HouseSalesFields.EMAIL.getValue(), "[email protected]"); maps.put(HouseSalesFields.DEPARTMENT.getValue(), "Residential Sales"); maps.put(HouseSalesFields.HOUSE_SALES_LIST.getValue(), RESOURCE_WORD_URL + "viewsheet_viewing_schedule.html"); //Replace Word Header WordFileUtils.replaceWordContentDocx(templateFilePath, destinationFilePath, maps); Document doc = new Document(destinationFilePath); List<String> dataList = Lists.newArrayList(); dataList.add("1"); dataList.add("2"); dataList.add("3"); dataList.add("4"); dataList.add("5"); dataList.add("6"); dataList.add("7"); dataList.add("8"); dataList.add("9"); dataList.add("10"); dataList.add("11"); dataList.add("12"); // dataList.add("13"); for(int dataNum=1;dataNum<=dataList.size(); dataNum++) { Section sec = doc.getSections().get(0); Table table = sec.addTable(); int rowsNum = 5; int columnsNum = 7; int borders = 2; if(dataNum == dataList.size()){ --rowsNum; --borders; } if (getNextMultipleOfFour(dataNum)) { // 是4的倍數 if(dataNum != dataList.size()){ ++rowsNum; ++borders; } table.resetCells(rowsNum, columnsNum);//指定嵌套表格行數、列數 }else{ table.resetCells(rowsNum, columnsNum);//指定嵌套表格行數、列數 } //最後一行添加下邊框 for(int i=0;i<columnsNum;i++){ TableCell tcell = table.getRows().get(rowsNum-borders).getCells().get(i); tcell.getCellFormat().getBorders().getBottom().setBorderType(BorderStyle.Single); } table.setPreferredWidth(new PreferredWidth(WidthType.Percentage, (short) 100)); table.getRows().get(0).setHeight(50f); // table.getRows().get(1).setHeight(10f); table.getRows().get(2).setHeight(38f); table.getRows().get(3).setHeight(30f); table.getTableFormat().setHorizontalAlignment(RowAlignment.Center);//設置嵌套表格在單元格中的對齊方式 int setWidth = 300; table.getRows().get(rowsNum-1).getCells().get(1).setWidth(100); table.getRows().get(rowsNum-1).getCells().get(2).setWidth(setWidth); table.getRows().get(rowsNum-1).getCells().get(3).setWidth(setWidth); table.getRows().get(rowsNum-1).getCells().get(4).setWidth(setWidth); table.getRows().get(rowsNum-1).getCells().get(5).setWidth(setWidth); // table.getRows().get(0).getCells().get(6).setWidth(10); table.applyHorizontalMerge(0, 2, 4); table.applyHorizontalMerge(3, 2, 5); table.applyVerticalMerge(1,0,1); table.applyVerticalMerge(2,1,2); table.applyVerticalMerge(3,1,2); table.applyVerticalMerge(4,1,2); table.applyVerticalMerge(5,1,2); DocPicture picture = (DocPicture) table.getRows().get(0).getCells().get(1).addParagraph().appendPicture("D://Property Photo.png"); picture.setHorizontalAlignment(ShapeHorizontalAlignment.Left); picture.setWidth(98f); picture.setHeight(60f); //填充數組內容到嵌套表格 for (int i = 0; i < rowsNum; i++) { TableRow dataRow = table.getRows().get(i); for (int j = 0; j < columnsNum; j++) { Paragraph paragraph = dataRow.getCells().get(j).addParagraph(); // 插入HTML內容到表格 if(i==0 && j==5){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TITLE_FONT_SIZE + "px;line-height:0.2px;\"><b>HKD400,000</b><br></dev>" + "<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;\">Saleable(實用)<br></dev>" + "<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TITLE_FONT_SIZE + "px;\"><b>3,461 平方尺</b><br></dev>" + "<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;\">@HKD115.57 平方尺</dev></dev>"; paragraph.appendHTML(html); } if(i==0 && j==2){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TITLE_FONT_SIZE + "px;line-height:0.2px;\"><b>34 Island Road(香島道34號),House B(B屋)</b><br></dev>" + "<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;\">34 Island Road,34 Island Road,Deep Water Bay.Hong Kong<br>" + "<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;\">(香港南區深水灣香道道34號)<br></dev>"; paragraph.appendHTML(html); } if(i==1 && j==2){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;line-height:0.2px;\"><b>ROOM DETAILS </b>房間詳情<br>" + "房間詳情內容<br>" + "房間詳情內容<br>" + "房間詳情內容" + "</dev>"; paragraph.appendHTML(html); } if(i==1 && j==3){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;line-height:0.2px;\"><b>KEY FEATURES </b>主要特色<br>" + POINT + "主要特色內容<br>" + POINT + "主要特色內容<br>" + POINT + "主要特色內容<br>" + "</dev>"; paragraph.appendHTML(html); } if(i==1 && j==4){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;line-height:0.2px;\"><br>" + POINT + "內容<br>" + POINT + "內容<br>" + POINT + "內容" + "</dev>"; paragraph.appendHTML(html); } if(i==1 && j==5){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;line-height:0.2px;\"><b>PROPERTY DETAIL </b>物業詳情<br>" + "Govt Rate 差餉:全包<br>" + "Mgmt Fee 管理費:全包<br>" + "Possession Type: Vacant 交吉" + "</dev>"; paragraph.appendHTML(html); } if(i==2 && j==1){ paragraph.appendHTML("<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;\">Listing ID 參考編號:<br>2023313714</dev>"); } if(i==3 && j==1){ paragraph.appendHTML("<dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;background-color:#c90c0f;color:#FFFFFF\"> Lead Agent 首席代理 </dev>"); } if(i==3 && j==2){ String html = "<dev><dev style=\"font-family:" + FONT_FAMILY + ";font-size:" + TEXT_FONT_SIZE + "px;\"><b>REMARK </b>評論<br>" + "" + "</dev>"; paragraph.appendHTML(html); } ParagraphFormat format = paragraph.getFormat(); format.setLineSpacingRule(LineSpacingRule.Exactly); format.setLineSpacing(10f); } } } //保存文檔 doc.saveToFile(destinationFilePath,FileFormat.Docx); //download file File destinationFile = new File(destinationFilePath); FileUtils.downLoadFile(response, destinationFileName, destinationFile); //Check file is exists. if (destinationFile.exists()) { //File delete. boolean isDeleted = destinationFile.delete(); if (isDeleted) { log.info("Delete word file done:" + destinationFilePath); } else { log.info("Delete word file failed:" + destinationFilePath); } } else { log.info("Delete word file is not exists:" + destinationFilePath); } } }
public static void downLoadFile(HttpServletResponse response, String fileName, File file) throws IOException { //文件下載 //設置兩個響應頭文件 //給瀏覽器設置響應頭:Content-Disposition 告訴瀏覽器以附件的形式打開這個文件 response.setHeader("Content-Disposition", "attachment;filename=" + fileName); //給瀏覽器設置響應頭:文件類型 response.setContentType(mimetype); //獲取文件的mimeType response.setContentType(new MimetypesFileTypeMap().getContentType(file)); //3.設置兩個流,進行流拷貝操作 //讀取文件資源 FileInputStream fileInputStream = new FileInputStream(file); //從response中獲取輸出流 ServletOutputStream outputStream = response.getOutputStream(); //流拷貝 int len = 0; byte[] arr = new byte[1024]; while ((len = fileInputStream.read(arr)) != -1) { outputStream.write(arr, 0, len); } //隨着response關閉 fileInputStream.close(); outputStream.close(); }
public static void replaceWordContentDocx(String templateFilePath, String destinationFilePath, Map<String, String> replaceMaps) throws IOException{ //is exists? File templateFile = new File(templateFilePath); if(!templateFile.exists()){ log.error("The " + templateFilePath + " template is not exists."); } //copy copyWord(templateFilePath, destinationFilePath); //set data for(String searchKey : replaceMaps.keySet()){ replaceText(destinationFilePath, searchKey, replaceMaps.get(searchKey)); } log.info("replaceWordContentForDoc ===== Done ====="); }
import org.apache.poi.xwpf.usermodel.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.Map; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableRow; /** * @Author Robert * @Date 2024/4/7 11:55 * @Version 1.0 * @Param * @return */ public class WordFileUtils { private static final Logger log = LoggerFactory.getLogger(ListingServiceImpl.class); public static void replaceWordContentDocxUnderline(String templateFilePath, String destinationFilePath, Map<String, String> replaceMaps) throws IOException{ //is exists? File templateFile = new File(templateFilePath); if(!templateFile.exists()){ log.error("The " + templateFilePath + " template is not exists."); } //copy copyWord(templateFilePath, destinationFilePath); //set data for(String searchKey : replaceMaps.keySet()){ replaceTextUnderline(destinationFilePath, searchKey, replaceMaps.get(searchKey)); } log.info("replaceWordContentForDoc ===== Done ====="); } public static void replaceWordContentDocx(String templateFilePath, String destinationFilePath, Map<String, String> replaceMaps) throws IOException{ //is exists? File templateFile = new File(templateFilePath); if(!templateFile.exists()){ log.error("The " + templateFilePath + " template is not exists."); } //copy copyWord(templateFilePath, destinationFilePath); //set data for(String searchKey : replaceMaps.keySet()){ replaceText(destinationFilePath, searchKey, replaceMaps.get(searchKey)); } log.info("replaceWordContentForDoc ===== Done ====="); } public static void copyWord(String templateFilePath, String destinationFilePath){ try (InputStream in = new FileInputStream(templateFilePath); XWPFDocument document = new XWPFDocument(in); OutputStream out = new FileOutputStream(destinationFilePath)) { document.write(out); out.close(); in.close(); log.info("Word copy Done!"); } catch (IOException e) { log.info("Word copy Faild:" + e.getMessage()); e.printStackTrace(); } } public static void replaceTextUnderline(String filePath, String textToFind, String textToReplace) throws IOException { FileInputStream fis = new FileInputStream(filePath); XWPFDocument document = new XWPFDocument(fis); // 遍歷文檔中的段落並替換文本 for (XWPFParagraph para : document.getParagraphs()) { replaceInParaUnderline(para, textToFind, textToReplace); } // 遍歷文檔中的表格 for (XWPFTable table : document.getTables()) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph para : cell.getParagraphs()) { replaceInPara(para, textToFind, textToReplace); } } } } // 關閉輸入流並寫入新內容到文件 fis.close(); FileOutputStream fos = new FileOutputStream(filePath); document.write(fos); fos.close(); } public static void replaceText(String filePath, String textToFind, String textToReplace) throws IOException { FileInputStream fis = new FileInputStream(filePath); XWPFDocument document = new XWPFDocument(fis); // 遍歷文檔中的段落並替換文本 for (XWPFParagraph para : document.getParagraphs()) { replaceInPara(para, textToFind, textToReplace); replaceInParaHeader(document, textToFind, textToReplace); } // 遍歷文檔中的表格 for (XWPFTable table : document.getTables()) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph para : cell.getParagraphs()) { replaceInPara(para, textToFind, textToReplace); } } } } // 關閉輸入流並寫入新內容到文件 fis.close(); FileOutputStream fos = new FileOutputStream(filePath); document.write(fos); fos.close(); } private static void replaceInPara(XWPFParagraph para, String textToFind, String textToReplace) { for (XWPFRun run : para.getRuns()) { String text = run.getText(run.getTextPosition()); String textToFindEscape = notEmpty(textToFind) && textToFind.contains("\\") ? textToFind.replace("\\","") : textToFind; if (text != null && text.contains(textToFindEscape) && textToReplace != null && textToReplace != null) { log.info("replaceInPara:text=" + text + "|textToFind="+textToFind + "|textToReplace=" + textToReplace); text = text.replaceAll(textToFind, textToReplace); run.setText(text, 0); } } } private static void replaceInParaUnderline(XWPFParagraph para, String textToFind, String textToReplace) { for (XWPFRun run : para.getRuns()) { String text = run.getText(run.getTextPosition()); String textToFindEscape = notEmpty(textToFind) && textToFind.contains("\\") ? textToFind.replace("\\","") : textToFind; if (text != null && text.contains(textToFindEscape) && textToReplace != null && textToReplace != null) { log.info("replaceInParaUnderline:text=" + text + "|textToFind="+textToFind + "|textToReplace=" + textToReplace); text = text.replaceAll(textToFind, textToReplace); run.setText(text, 0); run.setUnderline(UnderlinePatterns.SINGLE); } } } private static void replaceInParaHeader(XWPFDocument document, String textToFind, String textToReplace) { List<XWPFHeader> headers = document.getHeaderList(); // 遍歷每個段落 for (XWPFHeader header : headers) { List<XWPFParagraph> paragraphs = header.getParagraphs(); for(XWPFParagraph paragraph:paragraphs){ replaceInPara(paragraph, textToFind, textToReplace); // 遍歷所有段落,找到表格 for (IBodyElement element : header.getBodyElements()) { if (element instanceof XWPFTable) { XWPFTable table = (XWPFTable) element; for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { for (XWPFParagraph para : cell.getParagraphs()) { replaceInPara(para, textToFind, textToReplace); } } } } } } } } public static Boolean getNextMultipleOfFour(int number) { // 使用%運算符找到餘數,然後通過加上足夠的4的倍數來獲取下一個4的倍數 int remainder = number % 4; if (remainder == 0 && number != 0) { // 如果本身就是4的倍數,則直接返回 return true; } else { // 不是4的倍數,則加上足夠的4的倍數 return false; } } }