庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

iText操作PDF问题总结

Posted on 2007-04-02 15:52 dennis 阅读(12768) 评论(9)  编辑  收藏 所属分类: java
    这个星期我的任务就是处理一些报表的打印问题,因为我算项目组里对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上的,所以有很多<br>标签,可以使用正则表达式替换成"\n"
"<br>1.测试<br>2.测试2".replaceAll("<br>|</br>","\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之前。

评论

# re: iText操作PDF问题总结  回复  更多评论   

2007-04-04 08:51 by 祎恬凡
对于你开始那个结构,你又没想过用子报表将其分割!!!
不知道子报表是否可行!!

# re: iText操作PDF问题总结[未登录]  回复  更多评论   

2007-04-04 08:56 by dennis
@祎恬凡

这个我早就试过,没办法做到的,因为跨行的单行高度与子报表关联,又没办法动态设置行高

# re: iText操作PDF问题总结  回复  更多评论   

2007-04-30 10:30 by 叶之韵律
终于找到一个稍微有点建设性的文章了,可惜还是没提到我需要解决的问题:动态换行。简单地说就是,如果某一行有个字段的数据过长,我想把该字段自动换行,同时把该行的所有字段的高度撑大。我在iReport把Stretch Type的Relavive to band height选上,并且把Stretch with overflow给勾上后,预览JrViewer时保存为PDF文件,如果有一个字段太长了,就可以自动把该字段所在行的整行换行;但如果用代码输出为pdf时,需要换行的字段却不显示出来,并且整行也不换行了。请教一下,这个问题应该怎么解决呢?

# re: iText操作PDF问题总结  回复  更多评论   

2007-05-08 15:15 by dennis
@叶之韵律
似乎没办法解决,我的处理方法就是将field的length和height相应增大,字段总有一个最大长度!-_-

# re: iText操作PDF问题总结  回复  更多评论   

2007-06-28 10:07 by Melinda
叶之韵律:
属性中的position type应该选成float

# re: iText操作PDF问题总结  回复  更多评论   

2008-06-06 11:12 by xmzzy
请问下,用iReport导出到word后,原来<textarea>中的大段文字的换行符会在导出的word里显示成<br>,这个问题如何解决?

# re: iText操作PDF问题总结  回复  更多评论   

2012-07-09 17:08 by 闫晓盼
如何设置表头内容???

# re: iText操作PDF问题总结  回复  更多评论   

2012-07-09 17:13 by 闫晓盼
明白了……对自己无解了,嘿嘿,

# 换行  回复  更多评论   

2013-08-09 13:56 by 张志明
换行成两行 的高度是一行的高度两倍怎么换

只有注册用户登录后才能发表评论。


网站导航: