首頁>技術>

PDF操作類庫 iText

iText是一個非常著名的能夠快速產生PDF檔案的Java類庫。支援文字,表格,圖形的操作,可以方便的跟 Servlet 進行結合

iText的更新變化很大,早期版本在PDF樣式上可能會有瑕疵,所有我使用的最新的5.5.6包

1.新增Maven依賴

itext核心包 和xmlworder字型包

<dependency>      <groupId>com.itextpdf</groupId>     <artifactId>itextpdf</artifactId>     <version>5.5.6</version></dependency><dependency>   <groupId>com.itextpdf.tool</groupId>   <artifactId>xmlworker</artifactId>   <version>5.5.6</version></dependency>
2.直接生成pdf

非常簡單,用文字建立段落等即可,設定好字型、間距、對齊方式等等即可,弄個Hello World 的例子。

package iText;import com.itextpdf.text.*;import com.itextpdf.text.pdf.BaseFont;import com.itextpdf.text.pdf.PdfPHeaderCell;import com.itextpdf.text.pdf.PdfPTable;import com.itextpdf.text.pdf.PdfWriter;import org.junit.Test;import java.io.FileOutputStream;/** * author wangnian * date 2016/4/1 */public class PdfDemo_1 {    private static void create() throws Exception {        // 建立一個文件(預設大小A4,邊距36, 36, 36, 36)        Document document = new Document();          // 設定文件大小          document.setPageSize(PageSize.A4);          // 設定邊距,單位都是畫素,換算大約1釐米=28.33畫素          document.setMargins(50, 50, 50, 50);        // 設定pdf生成的路徑        FileOutputStream fileOutputStream= new FileOutputStream("D:/demo.pdf");        // 建立writer,透過writer將文件寫入磁碟        PdfWriter writer = PdfWriter.getInstance(document,fileOutputStream);        // demo        String title = "打油詩";        String content = "大學校園真開放,宿舍竟能打麻將。東南西北皆可碰,哪管父母心血濃。";            // 定義字型          FontFactoryImp ffi = new FontFactoryImp();          // 註冊全部預設字型目錄,windows會自動找fonts資料夾的,返回值為註冊到了多少字型          ffi.registerDirectories();          // 獲取字型,其實不用這麼麻煩,後面有簡單方法          Font font = ffi.getFont("宋體",BaseFont.IDENTITY_H,BaseFont.EMBEDDED, 12, Font.UNDEFINED, null);              // 開啟文件,只有開啟後才能往裡面加東西          document.open();              // 設定作者          document.addAuthor("校園作者");        // 設定建立者          document.addCreator("wangnian");        // 設定主題          document.addSubject("測試");        // 設定標題          document.addTitle("打油詩");            // 增加一個段落          document.add(new Paragraph(title, font));          document.add(new Paragraph(content, font));          document.add(new Paragraph("\n\r", font));              // 建立表格,5列的表格          PdfPTable table = new PdfPTable(4);          table.setTotalWidth(PageSize.A4.getWidth()- 100);          table.setLockedWidth(true);          // 建立頭          PdfPHeaderCell header = new PdfPHeaderCell();          header.addElement(new Paragraph(title, font));          header.setColspan(4);          table.addCell(header);          // 新增內容          table.addCell(new Paragraph("大學校園真開放",font));        table.addCell(new Paragraph("宿舍竟能打麻將",font));        table.addCell(new Paragraph("東南西北皆可碰",font));        table.addCell(new Paragraph("哪管父母心血濃",font));            document.add(table);          // 關閉文件,才能輸出          document.close();          writer.close();     }      @Test   public  void test()  {       try {           create();           System.out.println("生成成功");       }catch (Exception ex){           System.out.println("檔案路徑錯誤或者許可權不夠");       }   }}
3.字型

我們專案文書字型比較特殊,比如用到了宋體(99%都這個吧)、華文仿宋(安裝office後自帶)、仿宋_GB2312等,於是就研究了一下pdf字型,網上有很多方法使用中文字型,其實5.0版以後的iText加入字型還是很方便的。

package iText;import java.io.FileOutputStream;import com.itextpdf.text.Document;  import com.itextpdf.text.Font;  import com.itextpdf.text.FontFactoryImp;  import com.itextpdf.text.Paragraph;  import com.itextpdf.text.pdf.PdfWriter;  import com.itextpdf.tool.xml.XMLWorkerFontProvider;import org.junit.Test;/**  * 字型  *  * author wangnian * date 2016/4/1 *  */  public class PdfDemo_2 {          public static void create() throws Exception {          Document document = new Document();          PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream("D:/demo2.pdf"));        String title = "涼州詞";          String content = "黃河遠上白雲間,一片孤城萬仞山。羌笛何須怨楊柳,春風不度玉門關。";          document.open();          document.add(new Paragraph(title, getFont("方正蘭亭黑簡體")));        document.add(new Paragraph(content, getFont("迷你簡娃娃篆")));        document.close();          writer.close();      }          private static Font getFont(String fontName) {          // xmlworker主要功能是html轉pdf用的,非常好用,也是itext官方的              // 這個是xmlworker提供的獲取字型方法,很方便,對中文支援很好          FontFactoryImp fp = new XMLWorkerFontProvider();        // 註冊指定的字型目錄,預設構造方法中會註冊全部目錄,我這裡註冊了src/font目錄          fp.registerDirectory(PdfDemo_2.class.getClassLoader().getResource("weiruanyahei").getFile(), true);            // 最好的地方是直接支援獲取中文的名字          return fp.getFont(fontName);              // 當然,最好的方法是自己繼承XMLWorkerFontProvider,提供一些常用字型,簡單方便      }      @Test    public void test() throws Exception {        create();        System.out.println("生成成功");    }  }

xmlworker的XMLWorkerFontProvider提供了很方便的獲取字型方法:

1.註冊一個資料夾,裡面有哪些字型都可以,比如我demo中的字型

2.使用getFont(字型名)即可獲得,不過字型名從哪來的呢

4.頁首頁尾

iText5中並沒有之前版本HeaderFooter物件設定頁首和頁尾,可以利用PdfPageEvent來完成頁首頁尾的設定工作。

PdfPageEvent提供了幾個pdf在建立時的事件,頁首頁尾就是在每頁載入完寫入的。

每一頁加個頁碼還是很簡單的,但是總頁碼就麻煩了,iText是流模式的寫入內容,只有寫到最後,才能知道有多少頁,那麼顯示總頁數就麻煩了,不過麻煩不代表不可能。

其實iText僅在呼叫釋放模板方法後才將PdfTemplate寫入到OutputStream中,否則物件將一直儲存在記憶體中,直到關閉文件。

所以我們可以在最後關閉文件前,使用PdfTemplate寫入總頁碼。可以理解成先寫個佔位符,然後統一替換。

還是HelloWorld例子:

package iText;import com.itextpdf.text.*;import com.itextpdf.text.pdf.*;import com.itextpdf.tool.xml.XMLWorkerFontProvider;/** * iText5中並沒有之前版本HeaderFooter物件設定頁首和頁尾<br>  * 不過,可以利用PdfPageEventHelper來完成頁首頁尾的設定工作。<br>  * 就是在頁面完成但寫入內容之前觸發事件,插入頁首、頁尾、水印等。<br>  *  * author wangnian * date 2016/4/1 *  */     public class MyHeaderFooter extends PdfPageEventHelper {    Font font = new XMLWorkerFontProvider().getFont("宋體", 12, BaseColor.RED);    // 總頁數      PdfTemplate totalPage;    // 開啟文件時,建立一個總頁數的模版      public void onOpenDocument(PdfWriter writer, Document document) {        PdfContentByte cb =writer.getDirectContent();        totalPage = cb.createTemplate(30, 16);      }      // 一頁載入完成觸發,寫入頁首和頁尾      public void onEndPage(PdfWriter writer, Document document) {        PdfPTable table = new PdfPTable(3);        try {              table.setTotalWidth(PageSize.A4.getWidth() - 100);            table.setWidths(new int[] { 24, 24, 3});              table.setLockedWidth(true);             table.getDefaultCell().setFixedHeight(-10);             table.getDefaultCell().setBorder(Rectangle.BOTTOM);               table.addCell(new Paragraph("我是文字", font));// 可以直接使用addCell(str),不過不能指定字型,中文無法顯示           table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);              table.addCell(new Paragraph("第" + writer.getPageNumber() + "頁/", font));              // 總頁數              PdfPCell cell = new PdfPCell(Image.getInstance(totalPage));              cell.setBorder(Rectangle.BOTTOM);              table.addCell(cell);              // 將頁首寫到document中,位置可以指定,指定到下面就是頁尾              table.writeSelectedRows(0, -1, 50,PageSize.A4.getHeight() - 20, writer.getDirectContent());          } catch (Exception de) {              throw new ExceptionConverter(de);          }      }         // 全部完成後,將總頁數的pdf模版寫到指定位置      public void onCloseDocument(PdfWriter writer,Document document) {          String text = "總" + (writer.getPageNumber() - 1) + "頁";          ColumnText.showTextAligned(totalPage, Element.ALIGN_LEFT, new Paragraph(text,font), 2, 2, 0);      }  }
package iText;import java.io.FileOutputStream;import com.itextpdf.text.BaseColor;  import com.itextpdf.text.Document;  import com.itextpdf.text.Element;  import com.itextpdf.text.ExceptionConverter;  import com.itextpdf.text.Font;  import com.itextpdf.text.Image;  import com.itextpdf.text.PageSize;  import com.itextpdf.text.Paragraph;  import com.itextpdf.text.Rectangle;  import com.itextpdf.text.pdf.ColumnText;  import com.itextpdf.text.pdf.PdfContentByte;  import com.itextpdf.text.pdf.PdfPCell;  import com.itextpdf.text.pdf.PdfPTable;  import com.itextpdf.text.pdf.PdfPageEventHelper;  import com.itextpdf.text.pdf.PdfTemplate;  import com.itextpdf.text.pdf.PdfWriter;  import com.itextpdf.tool.xml.XMLWorkerFontProvider;import org.junit.Test;/**  * 頁首、頁尾  * author wangnian * date 2016/4/1 */public class PdfDemo_3 {         public static void create() throws Exception {          Document document = new Document(PageSize.A4, 50, 50, 50, 50);        PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream("d:/demo3.pdf"));           // 增加頁首頁尾          writer.setPageEvent(new MyHeaderFooter());           String title = "涼州詞";          String content = "黃河遠上白雲間,一片孤城萬仞山。羌笛何須怨楊柳,春風不度玉門關。";          document.open();             Font font = new XMLWorkerFontProvider().getFont("宋體");        for (int i = 0; i <100; i++) {              document.add(new Paragraph(title, font));            document.add(new Paragraph(content,font));              document.add(new Paragraph("\n"));          }          document.close();          writer.close();      }      @Test    public  void test() throws Exception {        create();        System.out.println("生成成功");    }  }
5.html轉pdf

結果還不錯,雖然可以滿足我們的要求,但是比較複雜,動態建立一個個的表格和內容過於繁瑣,方法太粗暴了,使用者 的文件內容或格式變化,就要修改程式了。

先建立html,然後轉換成pdf,demo如下:

package iText;import java.io.ByteArrayInputStream;import java.io.FileOutputStream;  import java.io.InputStream;  import java.io.OutputStream;  import com.itextpdf.text.Document;  import com.itextpdf.text.pdf.PdfWriter;  import com.itextpdf.tool.xml.XMLWorkerHelper;import org.junit.Test;/**  * html轉pdf * author wangnian * date 2016/4/1 *  */  public class PdfDemo_4 {          public static void create() throws Exception {              // html中字型非常鬱悶          // 1. html中不指定字型,則預設使用英文字型,中文會不顯示。          // 2. html中指定的字型必須是英文名稱,如宋體:font-family:SimSun;          // 3. html中不能指定自定義字型,必須指定itext支援的字型,還好itext支援字型比較多,常見作業系統帶的都支援          // 4. 暫沒有找到如何html中支援自定義字型方法,網上都是修改原始碼實現預設字型中文,也很重要              StringBuilder html = new StringBuilder();        html.append("<html>");          html.append("<body style='font-size:20px;font-family:SimSun;'>");        html.append("<table width='19cm'border='1' cellpadding='0' cellspacing='0'>");          html.append("<tr>");          html.append("<td colspan='2'>涼州詞</td>");          html.append("</tr>");          html.append("<tr>");          html.append("<td>黃河遠上白雲間,一片孤城萬仞山。</td>");          html.append("<td>羌笛何須怨楊柳,春風不度玉門關。</td>");          html.append("</tr>");          html.append("</table>");          html.append("</body>");          html.append("</html>");              InputStream is = new ByteArrayInputStream(html.toString().getBytes());            OutputStream os = new FileOutputStream("D:/demo4.pdf");        Document document = new Document();              PdfWriter writer = PdfWriter.getInstance(document,os);              document.open();              // 將html轉pdf          XMLWorkerHelper.getInstance().parseXHtml(writer,document, is);              document.close();      }      @Test    public  void test() throws Exception {        create();        System.out.println("生成成功");    }  }

此處使用了XmlWorker,XmlWorker也是iText官方的,目前和iText版本一起更新,可以講XHTML轉換成pdf,支援大部分樣式和標籤,是大部分哦,不是全部。

目前我們就用的這個方式,寫好html文件,使用時動態替換html中的標記位,然後生成pdf。

使用XHTML轉pdf要注意的地方:

1. html中不指定字型,則預設使用英文字型,中文會不顯示;

2. html中指定的字型必須是英文名稱;如宋體:font-family:SimSun;正確 font-family:宋體;則錯誤,竟然unicode也不行。

3. html中不能指定自定義字型(比如上文中的方正蘭亭黑),但是itext一般作業系統的字型都支援,如果ubuntu上沒有微軟雅 黑,可以從windows下複製雅黑字型Yahei.ttf 放進來ubuntu上/usr/share/fonts/路徑。

4. pdf中新增圖片也非常簡單,例如:<img src='d:/1.jpg'/>,就可以了。

5. XHTML不是HTML,所以任何標籤都要完整結束,比如<br>錯誤,必須<br/>才行。

寫一個html模版很簡單,需要對html和css熟練,調生成的樣式部分比較麻煩(比如文字多了會切掉,不切會影響整體樣式,表格線有粗有細,xmlworker不支援全部css等),一般A4紙都是釐米單位的,html中最好也使用釐米,處理簡單點。

16
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • SpringCloud 和 Dubbo 哪個會先被 K8S 淘汰?JAVA大軍,如何快速掌握K8S?