iText操作PDF問題總結

dennis_zane發表於2007-04-02
    這個星期我的任務就是處理一些報表的列印問題,因為我算專案組裡對jasperreport比較熟悉的了,這個東東也是我引進到這個專案。ireport畫報表,使用struts的action輸出PDF到瀏覽器,這是我們目前的解決方案。今天遇到一個ireport解決不了的要求——合併單元格。類似下面這樣的表結構:
----------------------------------------------
          |          |__c_____________
   dept   | value    |__b_____________
          |          |  a
--------------------------------------------------------
也就是需要跨行,跨列!-_-。在html表格中解決這個很簡單,只要設定單元格的colspan和rowspan即可。我在ireport沒有找到解決方案,如果您知道,請不吝賜教。查詢資料弄了兩個小時沒進展,決定自己用iText寫吧,通過google、baidu資料順利達到了我的要求,僅在此記錄下遇到的問題和解決方法。

一。一個HelloWorld例項:
package com.lowagie.examples.general;

import java.io.FileOutputStream;
import java.io.IOException;

import com.lowagie.text.*;
import com.lowagie.text.pdf.PdfWriter;

/**
 * Generates a simple 'Hello World' PDF file.
 *
 * 
@author blowagie
 
*/

public class HelloWorld {

/**
 * Generates a PDF file with the text 'Hello World'
 *
 * 
@param args no arguments needed here
 
*/
public static void main(String[] args) {

System.out.println(
"Hello World");

// step a: creation of a document-object
      Document document = new Document();
try {
// step b:
// we create a writer that listens to the document
// and directs a PDF-stream to a file
        PdfWriter.getInstance(document,new FileOutputStream("HelloWorld.pdf"));

// step c: we open the document
        document.open();
// step d: we add a paragraph to the document
        document.add(new Paragraph("Hello World"));
catch (DocumentException de) {
System.err.println(de.getMessage());
catch (IOException ioe) {
System.err.println(ioe.getMessage());
}

// step e: we close the document
        document.close();
}
}
可以看到一個PDF檔案的輸出,總共只需要5個步驟
a.建立一個Document例項
Document document = new Document();
b.將Document例項和檔案輸出流用PdfWriter類繫結在一起
PdfWriter.getInstance(document,new FileOutputStream("HelloWorld.pdf"));
c.開啟文件
document.open();
d.在文件中新增文字
document.add(new Paragraph("Hello World"));
e.關閉文件
document.close();
這樣5個步驟,就可以生成一個PDF文件了。

二。如何使用jsp、servlet輸出iText生成的pdf?
  如果每次都在服務端生成一個PDF檔案給使用者,不僅麻煩,而且浪費伺服器資源,最好的方法就是以二進位制流的形式輸送到客戶端。
1)JSP輸出:
<%@ page import="java.io.*,java.awt.Color,com.lowagie.text.*,com.lowagie.text.pdf.*"%>

<%
response.setContentType
"application/pdf" );
Document document 
= new Document();
ByteArrayOutputStream buffer
= new ByteArrayOutputStream();
PdfWriter writer
=
PdfWriter.getInstance( document, buffer );
document.open();
document.add(
new Paragraph("Hello World"));
document.close();
DataOutput output 
=
new DataOutputStream
( response.getOutputStream() );
byte[] bytes = buffer.toByteArray();
response.setContentLength(bytes.length);
forint i = 0;
< bytes.length;
i
++ )
{
output.writeByte( bytes[i] );
}
%>

2)servlet輸出,稍微改造下就可以使用在struts中:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
public void doGet
(HttpServletRequest request,
HttpServletResponse response)
throws IOException,ServletException
{
Document document 
=
new Document(PageSize.A4, 36,36,36,36);
ByteArrayOutputStream ba
= new ByteArrayOutputStream();
try
{
PdfWriter writer 
=
PdfWriter.getInstance(document, ba);
document.open();
document.add(
new
Paragraph(
"Hello World"));
}
catch(DocumentException de)
{
de.printStackTrace();
System.err.println
(
"A Document error:" +de.getMessage());
}
document.close();
response.setContentType
(
"application/pdf");
response.setContentLength(ba.size());
ServletOutputStream out
= response.getOutputStream();
ba.writeTo(out);
out.flush();
}


三。如何輸出中文?
    首先需要下載iTextAsian.jar包,可以到iText的主站上下,ireport也是需要這個包的。然後定義中文字型:
    private static final Font getChineseFont() {
        Font FontChinese 
= null;
        
try {
            BaseFont bfChinese 
= BaseFont.createFont("STSong-Light",
                    
"UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            FontChinese 
= new Font(bfChinese, 12, Font.NORMAL);
        } 
catch (DocumentException de) {
            System.err.println(de.getMessage());
        } 
catch (IOException ioe) {
            System.err.println(ioe.getMessage());
        }
        
return FontChinese;
    }

我將產生中文字型封裝在方法內,自定義一個段落PDFParagraph繼承自Paragraph,預設使用中文字型:
class PDFParagraph extends Paragraph {
        
public PDFParagraph(String content) {
            
super(content, getChineseFont());
        }
    }

使用的時候就可以簡化了:

Paragraph par = new PDFParagraph("你好");

四。如何設定PDF橫向顯示和列印?

Rectangle rectPageSize = new Rectangle(PageSize.A4);// 定義A4頁面大小
rectPageSize = rectPageSize.rotate();// 加上這句可以實現A4頁面的橫置
Document doc = new Document(rectPageSize,50,50,50,50);//4個引數,設定了頁面的4個邊距

五。如何設定跨行和跨列?

使用PdfPTable和PdfPCell 是沒辦法實現跨行的,只能跨列,要跨行使用com.lowagie.text.Table和com.lowagie.text.Cell類,Cell類有兩個方法:setRowspan()和setColspan()。

六。如何設定單元格邊界寬度?

Cell類的系列setBorderWidthXXXX()方法,比如setBorderWidthTop(),setBorderWidthRight()等

七。如何設定表頭?
希望每一頁都有表頭,可以通過設定表頭來實現。對於PdfPTable類來說,可以這樣設定:
PdfPTable table = new PdfPTable(3);
table.setHeaderRows(
2); // 設定了頭兩行為表格頭

而對於om.lowagie.text.Table類,需要在新增完所有表頭的單元格後加上一句程式碼:
table.endHeaders();

八。如何設定列寬?

Table table = new Table(8);
float[] widths = { 0.10f0.15f0.21f0.22f0.08f0.08f0.10f,
                    
0.06f };
table.setWidths(widths);

上面的程式碼設定了一個有8列的表格,通過一個float陣列設定列寬,這裡是百分比。

九。單元格內段落文字居中和換行?
居中通過Cell類來設定,一開始我以為設定段落對齊就可以了,沒想到是需要設定單元格:

cell.setHorizontalAlignment(Element.ALIGN_CENTER);


轉義符\n實現。在我的這個應用中,因為資料庫取出的資料是為了顯示在html上的,所以有很多
標籤,可以使用正規表示式替換成"\n"
"
1.測試
2.測試2
".replaceAll("
|
","\n");

十。如何顯示頁碼?
複雜的頁碼顯示和水印新增,需要使用到PdfPageEventHelper、PdfTemplate等輔助類,具體的例子參見iText的文件,如果只是為了簡單的顯示頁數,可以使用下面的程式碼:
            HeaderFooter footer = new HeaderFooter(new Phrase("頁碼:",getChineseFont()), true);
            footer.setBorder(Rectangle.NO_BORDER);
            document.setFooter(footer);
            document.open();
你可能注意到了,新增footer需要在document.open之前。

相關文章