OpenPdf组件替换Itext方案
OpenPdf組件替換Itext方案
- OpenPdf簡介
- 上代碼
- 1、pdf構建接口創建
- 2、實踐
- gitee項目地址
- 關于源碼本人還在研究中,后續會根據實際用到的進行相關代碼解析和案例講解
OpenPdf簡介
OpenPDF是一個Java庫,用于創建和編輯具有LGPL和MPL開源許可證的PDF文件。
官方git地址 鏈接: https://github.com/LibrePDF/OpenPDF
OpenPDF是iText的LGPL/MPL開源后續版本,它基于iText 4svn標記的fork、fork。我們歡迎其他開發者的貢獻。請隨時向這個GitHub存儲庫提交pull-requests和錯誤報告。
由于Itext 5.0和7.0要求使用它的項目也得開源,但這基本上不符合企業的項目。
上代碼
若項目已有itext的相關構建代碼,基本上就是重新引入包即可,相關接口的名字基本上一樣。
這里引用一個demo創建相關接口。
項目結構為SpringBoot +thymeleaf+maven項目,如圖所示:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version><relativePath/></parent><groupId>com.demo</groupId><artifactId>openPdfDemo</artifactId><version>0.0.1-SNAPSHOT</version><name>openPdfDemo</name><description>openPdfDemo</description><properties><java.version>8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.github.librepdf</groupId><artifactId>openpdf</artifactId><version>1.3.29</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>1、pdf構建接口創建
創建PdfCreator接口類,對操作步驟進行抽象化,方便后續擴展。
import com.lowagie.text.DocumentException;import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map;/*** pdf生成器接口*/ public interface PdfCreator {/*** 初始化** @return PdfCreator* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/PdfCreator init() throws DocumentException, IOException;/*** 構建pdf** @param map 導出數據參數* @return ByteArrayOutputStream* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/ByteArrayOutputStream creator(Map<String, Object> map) throws DocumentException, IOException;/*** 資源關閉** @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/void close() throws DocumentException, IOException; }創建 PdfCreateStrategy接口:
import com.lowagie.text.DocumentException;import java.io.IOException; import java.util.Map;/*** pdf 構建策略-用于不同pdf時指向各自的pdf構建實現*/ public interface PdfCreateStrategy {/*** 夠構建方法** @param map 導出數據所需參數* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/void execute(Map<String, Object> map) throws DocumentException, IOException; }創建抽象實現類AbstractPdfCreator,將共用部分進行提取
import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Font; import com.lowagie.text.Header; import com.lowagie.text.PageSize; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.PdfPageEventHelper; import com.lowagie.text.pdf.PdfWriter;import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map;/*** pdf構建抽象生成類*/ public abstract class AbstractPdfCreator implements PdfCreator, PdfCreateStrategy {/*** 頁框大小,采用的類型有 "crop", "trim", "art" and "bleed".,這里采用 "art"*/private static final String BOX_NAME = "art";/*** 文檔類*/protected Document document;/*** 基礎字體*/protected BaseFont baseFont;/*** 字體樣式*/protected Font font;/*** 字體大小*/protected int fontSize;/*** 頁面框架大小配置*/protected Rectangle pageSize;/*** 字節輸出流*/private ByteArrayOutputStream byteArrayOutputStream;/*** pdf構建操作轉為字節流** @param map 導出數據參數* @return ByteArrayOutputStream* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/@Overridepublic ByteArrayOutputStream creator(Map<String, Object> map) throws DocumentException, IOException {execute(map);close();return byteArrayOutputStream;}/*** pdf構建執行** @param map 導出數據所需參數* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/@Overridepublic abstract void execute(Map<String, Object> map) throws DocumentException, IOException;/*** 默認初始化** @return PdfCreator* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/@Overridepublic PdfCreator init() throws DocumentException, IOException {return init(baseFont, fontSize, font, pageSize, null);}/*** 自定義初始化監聽** @param helper 自定義文檔操作監聽* @return PdfCreator* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/public PdfCreator init(PdfPageEventHelper helper) throws DocumentException, IOException {return init(baseFont, fontSize, font, pageSize, helper);}/*** 自定義初始化參數** @param baseFont 基礎字體* @param fontSize 字體大小* @param font 字體樣式* @param rectangle 頁面大小設置* @param pdfPageEventHelper 文檔操作監聽* @return PdfCreator* @throws DocumentException 文檔操作異常* @throws IOException IO操作異常*/public PdfCreator init(BaseFont baseFont, int fontSize, Font font, Rectangle rectangle, PdfPageEventHelper pdfPageEventHelper) throws DocumentException, IOException {this.baseFont = null != baseFont ? baseFont : BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", Boolean.FALSE);this.fontSize = 0 != fontSize ? fontSize : Header.PARAGRAPH;this.font = null != font ? font : new Font(this.baseFont, this.fontSize, Font.NORMAL);this.pageSize = null != pageSize ? pageSize : PageSize.A4;document = new Document();document.setMargins(30, 30, 50, 90);byteArrayOutputStream = new ByteArrayOutputStream();PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStream);writer.setBoxSize(BOX_NAME, this.pageSize);if (null != pdfPageEventHelper) {writer.setPageEvent(pdfPageEventHelper);}document.open();return this;}/*** 資源關閉** @throws DocumentException* @throws IOException IO操作異常*/@Overridepublic void close() throws IOException {document.close();byteArrayOutputStream.close();} }自定義監聽器PdfPageEventListener創建
import com.lowagie.text.Document; import com.lowagie.text.Element; import com.lowagie.text.Font; import com.lowagie.text.Phrase; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.CMYKColor; import com.lowagie.text.pdf.ColumnText; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfPageEventHelper; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; import org.springframework.util.StringUtils;import java.io.IOException;/*** pdf頁面自定義監聽器*/ public class PdfPageEventListener extends PdfPageEventHelper {/*** pdf標題字體大小*/private int headerFontSize;/*** pdf標題寬度*/private Float headerWidth;/*** pdf標題高度*/private Float headerHeight;/*** pdf標題模板*/private PdfTemplate headerTemplate;/*** pdf標題基礎字體*/private BaseFont headerBaseFont;/*** pdf標題字體樣式*/private Font headerFont;/*** pdf編號(一般左上角都會有類似流水號的編號,可有可無)*/private String noticeCode;/*** pdf簽字(可有可無,根據實際情況進行賦值)*/private String signature;/*** 構建器,進行基本信息初始化** @param headerFontSize pdf標題字體大小* @param headerWidth pdf標題寬度* @param headerHeight pdf標題高度* @param headerBaseFont pdf標題基礎字體* @param headerFont pdf標題字體樣式* @param noticeCode pdf編號* @param signature pdf簽字*/public PdfPageEventListener(int headerFontSize, Float headerWidth, Float headerHeight, BaseFont headerBaseFont, Font headerFont, String noticeCode, String signature) {this.headerFontSize = headerFontSize;this.headerWidth = headerWidth;this.headerHeight = headerHeight;this.headerBaseFont = headerBaseFont;this.headerFont = headerFont;this.noticeCode = noticeCode;this.signature = signature;}/*** pdf監視器構建** @return Builder*/public static Builder build() {return new Builder();}/*** 重寫文檔初始化的模板*/@Overridepublic void onOpenDocument(PdfWriter writer, Document document) {headerTemplate = writer.getDirectContent().createTemplate(headerWidth, headerHeight);}/*** 重寫每一頁結束事件,等所有內容加載完畢后,進行頁眉的數據加載和賦值*/@Overridepublic void onEndPage(PdfWriter writer, Document document) {try {if (headerBaseFont == null) {headerBaseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", Boolean.FALSE);}if (headerFont == null) {headerFont = new Font(headerBaseFont, headerFontSize, Font.NORMAL);}} catch (IOException e) {e.printStackTrace();}//獲取文檔內容PdfContentByte pdfContentByte = writer.getDirectContent();float left = document.left();float right = document.right();float top = document.top();float bottom = document.bottom();int pageNumber = writer.getPageNumber();String noticeCodeText = StringUtils.isEmpty(noticeCode) ? "" : "編號:" + noticeCode;String previousHeaderText = "第 " + pageNumber + " 頁 /共";Phrase headerPhrase = new Phrase(previousHeaderText, headerFont);Phrase noticePhrase = new Phrase(noticeCodeText, headerFont);Phrase signaturePhrase = new Phrase(signature, headerFont);float noticeLen = headerBaseFont.getWidthPoint(noticeCodeText, headerFontSize);float headerLen = headerBaseFont.getWidthPoint(previousHeaderText, headerFontSize);float signatureLen = headerBaseFont.getWidthPoint(signature, headerFontSize);float x0 = left + noticeLen / 2;float x1 = right - headerLen / 2 - 20F;float x2 = right - 20F;float x3 = right - signatureLen / 2;float y1 = top + 10F;float y2 = bottom - 40F;ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER, noticePhrase, x0, y1, Element.HEADER);ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER, headerPhrase, x1, y1, Element.HEADER);ColumnText.showTextAligned(pdfContentByte, Element.ALIGN_CENTER, signaturePhrase, x3, y2, Element.HEADER);pdfContentByte.addTemplate(headerTemplate, x2, y1);float lineY = top + 6f;CMYKColor magentaColor = new CMYKColor(1.f, 1.f, 1.f, 1.f);pdfContentByte.setColorStroke(magentaColor);pdfContentByte.moveTo(left, lineY);pdfContentByte.lineTo(left, lineY);pdfContentByte.closePathStroke();}/*** 文檔結束后,將總頁碼放在文檔上*/@Overridepublic void onCloseDocument(PdfWriter writer, Document document) {headerTemplate.beginText();headerTemplate.setFontAndSize(headerBaseFont, headerFontSize);int pageNumber = writer.getPageNumber() - 1;String behindHeaderText = " " + pageNumber + " 頁";headerTemplate.showText(behindHeaderText);headerTemplate.endText();headerTemplate.closePath();}/*** 自定義一個構建器,設置頁面相關信息*/public static class Builder {private int headerFontSize;private Float headerWidth;private Float headerHeight;private PdfTemplate headerTemplate;private BaseFont headerBaseFont;private Font headerFont;private String noticeCode;private String signature;Builder() {headerFontSize = Font.DEFAULTSIZE;headerWidth = 50F;headerHeight = 50F;try {headerBaseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", Boolean.FALSE);} catch (IOException e) {e.printStackTrace();}headerFont = new Font(headerBaseFont, headerFontSize, Font.NORMAL);noticeCode = "";signature = "";}public Builder setHeaderFontSize(int headerFontSize) {this.headerFontSize = headerFontSize;return this;}public Builder setHeaderWidth(Float headerWidth) {this.headerWidth = headerWidth;return this;}public Builder setHeaderHeight(Float headerHeight) {this.headerHeight = headerHeight;return this;}public Builder setHeaderTemplate(PdfTemplate headerTemplate) {this.headerTemplate = headerTemplate;return this;}public Builder setHeaderBaseFont(BaseFont headerBaseFont) {this.headerBaseFont = headerBaseFont;return this;}public Builder setHeaderFont(Font headerFont) {this.headerFont = headerFont;return this;}public Builder setNoticeCode(String noticeCode) {this.noticeCode = noticeCode;return this;}public Builder setSignature(String signature) {this.signature = signature;return this;}public PdfPageEventListener build() {return new PdfPageEventListener(headerFontSize, headerWidth, headerHeight, headerBaseFont, headerFont, noticeCode, signature);}} }2、實踐
比如我們模擬用戶信息打印。
創建用戶信息實體類UserModel
public class UserModel {private String name;private int age;private String sex;private String nativePlace;private String national;private String education;private String remark;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getNativePlace() {return nativePlace;}public void setNativePlace(String nativePlace) {this.nativePlace = nativePlace;}public String getNational() {return national;}public void setNational(String national) {this.national = national;}public String getEducation() {return education;}public void setEducation(String education) {this.education = education;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;} }創建適用于用戶信息打印的pdf構建實現類UserInfoPdfCreator
import com.demo.openpdfdemo.model.UserModel; import com.demo.openpdfdemo.pdfCreator.core.AbstractPdfCreator; import com.demo.openpdfdemo.pdfCreator.core.HtmlParserUtil; import com.lowagie.text.Chunk; import com.lowagie.text.DocumentException; import com.lowagie.text.Element; import com.lowagie.text.Font; import com.lowagie.text.Paragraph; import com.lowagie.text.pdf.PdfPCell; import com.lowagie.text.pdf.PdfPTable; import org.springframework.util.CollectionUtils;import java.io.IOException; import java.util.List; import java.util.Map;/*** 用戶信息導出為pdf*/ public class UserInfoPdfCreator extends AbstractPdfCreator {@Overridepublic void execute(Map<String, Object> map) throws DocumentException, IOException {//map中可以存放實體類信息,以及其他各種邏輯數據信息等UserModel user = (UserModel) map.get("data");Font titleFont = new Font(baseFont, 14, Font.BOLD);Paragraph title = new Paragraph("用戶信息表", titleFont);title.setAlignment(Element.ALIGN_CENTER);title.setSpacingAfter(10F);document.add(title);Paragraph paragraphText = new Paragraph();Font font1 = new Font(baseFont, fontSize, Font.NORMAL);paragraphText.add(new Chunk("此表為用戶基本信息,請查看", font1));paragraphText.setAlignment(Element.ALIGN_LEFT);paragraphText.setFirstLineIndent(fontSize * 2);document.add(paragraphText);PdfPTable table = new PdfPTable(4);table.setTotalWidth(530);table.setWidths(new int[]{20, 30, 22, 28});table.setSpacingBefore(20);table.setWidthPercentage(100);table.setLockedWidth(true);Paragraph p1 = new Paragraph("姓名", font);PdfPCell cell1 = new PdfPCell(p1);cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell1);Paragraph p2 = new Paragraph(user.getName(), font);PdfPCell cell2 = new PdfPCell(p2);cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell2);Paragraph p3 = new Paragraph("年齡", font);PdfPCell cell3 = new PdfPCell(p3);cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell3);Paragraph p4 = new Paragraph(String.valueOf(user.getAge()), font);PdfPCell cell4 = new PdfPCell(p4);cell4.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell4);Paragraph p5 = new Paragraph("性別", font);PdfPCell cell5 = new PdfPCell(p5);cell5.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell5);Paragraph p6 = new Paragraph(user.getSex(), font);PdfPCell cell6 = new PdfPCell(p6);cell6.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell6);Paragraph p7 = new Paragraph("籍貫", font);PdfPCell cell7 = new PdfPCell(p7);cell7.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell7);Paragraph p8 = new Paragraph(user.getSex(), font);PdfPCell cell8 = new PdfPCell(p8);cell8.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell8);Paragraph p9 = new Paragraph("民族", font);PdfPCell cell9 = new PdfPCell(p9);cell9.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell9);Paragraph p10 = new Paragraph(user.getSex(), font);PdfPCell cell10 = new PdfPCell(p10);cell10.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell10);Paragraph p11 = new Paragraph("學歷", font);PdfPCell cell11 = new PdfPCell(p11);cell11.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell11);Paragraph p12 = new Paragraph(user.getSex(), font);PdfPCell cell12 = new PdfPCell(p12);cell12.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell12);Paragraph p13 = new Paragraph("備注", font);PdfPCell cell13 = new PdfPCell(p13);cell13.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell13);PdfPCell cell14 = new PdfPCell();List<Element> elements = HtmlParserUtil.htmlParseToElementList(user.getRemark(), font);if (!CollectionUtils.isEmpty(elements)) {for (Element element : elements) {cell14.addElement(element);}}cell14.setColspan(3);table.addCell(cell14);table.setSplitLate(false);document.add(table);} }實際項目中有的字段信息為富文本類型的,數據庫中存儲的是html片段,但這個跟整個html轉pdf還不一樣,比如這里的用戶的備注信息,這里要求這個備注信息插入到pdf中,其他字段不是html片段。官方提供的是通過HTMLWorker來實現,看源碼,提供的注釋也是少之甚少啊。
創建HtmlParserUtil工具類:
以上代碼可以看出對font進行了相關處理,若不處理的話,中文是不能正常顯示的。很多人就疑問itext可針對html進行字體注冊和樣式設置,這個沒有嗎?其實是有的;
再看源碼,第二個參數就是存儲樣式的,第三個參數是自定義字體注冊的實現類
worker.setInterfaceProps(interfaceProps);我們進一步看源碼
可以看到FontProvider是根據需要進行自定義實現類,它默認有一個實現類
這里面有很多方法,可以通過各種方式來將本地或者指定路徑的字體加載進來,如
我嘗試把本地的其他中文字體加載進來后,并且在html標簽上設置屬性font-family后,還是pdf無法顯示中文,這也是我為啥在工具類中直接獲取解析后的信息直接替換的原因。
好,繼續操作,創建UserService ,方法為printUserInfo,用戶信息可以自行實現加載,這里是模擬。
import com.demo.openpdfdemo.model.UserModel; import com.demo.openpdfdemo.pdfCreator.PdfPageEventListener; import com.demo.openpdfdemo.pdfCreator.UserInfoPdfCreator; import com.demo.openpdfdemo.pdfCreator.core.AbstractPdfCreator; import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.time.LocalDate; import java.util.HashMap; import java.util.Map;@Service public class UserService {/*** 用戶信息打印** @param response* @throws IOException*/public void printUserInfo(HttpServletResponse response) throws IOException {UserModel model = new UserModel();model.setName("張三");model.setAge(12);model.setEducation("本科");model.setNational("漢族");model.setSex("男");model.setNativePlace("北京市");model.setRemark("<p>此人學習習慣很好</p><p><strong>非常優秀</strong></p>愛好<ul><li>籃球</li><li>乒乓球</li></ul><table border=\"1\" style=\"border-collapse: collapse\"><tr><th style=\"width: 30%;color: aquamarine\">畢業學校</th> <th style=\"width: 30%\">專業</th> </tr><tr><td style=\"width: 30%\">清華</td> <td style=\"width: 30%\">計算機</td> </tr></table>");Map<String, Object> map = new HashMap<>();map.put("data", model);String signature = "簽字:________________" + "日期:" + LocalDate.now();PdfPageEventListener listener = PdfPageEventListener.build().setNoticeCode("0001").setSignature(signature).build();AbstractPdfCreator pdfCreator = new UserInfoPdfCreator();ByteArrayOutputStream outputStream = pdfCreator.init(listener).creator(map);response.reset();response.setHeader("Content-Type", "application/pdf");response.setHeader("content-disposition", "attachment;filename=111.pdf");response.setContentLength(outputStream.size());OutputStream output = response.getOutputStream();outputStream.writeTo(output);output.flush();output.close();} }創建controller :UserController
import com.demo.openpdfdemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletResponse; import java.io.IOException;@Controller @RequestMapping("/user") public class UserController {@Autowiredprivate UserService userService;@RequestMapping(path = "/userPrint", method = RequestMethod.POST)@ResponseBodypublic void userPrint(HttpServletResponse response) {try {userService.printUserInfo(response);} catch (IOException e) {e.printStackTrace();}}@RequestMapping("/hello")public String index(){return "index";} }創建一個頁面,模擬一下下載操作,創建index.html,默認是加載templetes文件夾的頁面,文件夾沒有自行創建即可。
application.yml配置,我這里簡單
server:port: 8080啟動服務,訪問
點擊下載,看一下內容。備注里的信息全是中文都可正常顯示。
gitee項目地址
鏈接: https://gitee.com/yang1112/openPdfDemo.git
關于源碼本人還在研究中,后續會根據實際用到的進行相關代碼解析和案例講解
總結
以上是生活随笔為你收集整理的OpenPdf组件替换Itext方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mac VirtualBox安装旧版ub
- 下一篇: pdfFactory Pro 不能被安装