[Java] PDF Form报表表单填写赋值

博客首页 » Java PDF Form报表表单填写赋值

发布于 27 Aug 2015 14:26
标签 blog
使用Java我们可以用PDF作为模板,通过填写赋值,生成复杂的报表

有如下几篇文章介绍使用方法

iText经验总结

http://helloxuweifu.iteye.com/blog/659042

AdobeSpring编程数据结构HTML
iText经验总结
因为前些日子在一个项目中用到了iText,稍有收获,便总结于此,以供他人所需。

iText是一个比较底层的pdf库,很多项目的pdf操作都是以它为基础的。像spring,以及另一个比较有名的报表工具jasperreports。简单的pdf报表输出用它比较合适,比较复杂的话使用起来就比较困难了,你要手工编写太多的代码。

比较好的是iText网站上提供相当多的示例代码,比较容易入门。我这里只说一些在它的文档里并没有直接讲到的东西。

1 关于Document

Document的几种构造函数:
public Document();
public Document(Rectangle pageSize);
public Document(Rectangle pageSize, int marginLeft, int marginRight, int marginTop, int marginBottom);
下面两种比较有用,如果是你想定义纸张大小和边缘的时候。对于Margin,iText上提到“You can also change the margins while you are adding content. Note that the changes will only be noticed on the NEXT page. If you want the margins mirrored (odd and even pages), you can do this with this method: setMarginMirroring(true). ”不过,对于table似乎并不好使。table并不会了理会你设定的margin,如果想改变它的magin还是需要去改变它的宽度(setWidth)。

2 pdf表单

使用PdfStamper是可以填充pdf表单的,这样就给出了一种很好的报表生成思路。
word制作报表样式>acrobat转pdf>itext填充数据—>输出pdf
这做非常简单,因为可以比较容易的控制pdf的样式。我对于Java的报表工具了解的并不多,不过在jasperreports,即使用GUI工具做一个样式比较复杂的报表也不是怎么容易。比如有那种斜线的表头,比较花哨的嵌套表格。这样的情况还是比较多见的,客户不会关系你实现起来是否困难。不过想要使用这种方式也有不足的地方。首先是acrobat把word转化成pdf的时候,格式总是保持不好,特别的是字体。然后是文件的体积这样生成的pdf会比直接用iText生成的pdf文件大很多,acrobat在pdf里加入了太多无用的信息。初次使用iText填充Adobe Designer生成的pdf表单时会有点小麻烦。在Designer中设计了一个name的text文本框的绑定名为name。照着iText中例子使用使用PdfStamper的setField方法去这样写form.setField("name", "XXXX");并不会成功。原因是Adobe Designer生成的表单名都是具有层次的,它可能是这个样子form1[0].#subform[0].name[0]。不过我们可以用一个方法把它们列出来,只要做一次就知道结构了,可以使用类似下面的代码:

PdfReader reader = new PdfReader("form.pdf"); 
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream("registered_flat.pdf")); 
AcroFields form = stamp.getAcroFields(); 
for (Iterator it = form.getFields().keySet().iterator(); it 
.hasNext();) { 
System.out.println(it.next()); 
}

如果直接用iText编程生成的表单就不会有这样的问题,设定的什么名字就是什么名字。
3 表单元素

pdf并不像html那样具有良好清晰的结构,而是一个有层次的文档类型。在它的maillist里,作者说明了iText虽然可以操作现存的pdf文件但是没办法去还原它的结构的。没办法像html一样,能从一个pdf文件获得一个清晰的“源文件”的。关于层次,可以从iText上得到详细的讲述,获取去看看pdf规范。表单和普通文本是不在一个层上的。没办法适用对待文本表各一样把它们简单的add进Document对象。获取一个cb直接去用绝对定位的方法可以加入表单元素,不过很多的时候因为排版并不能那么简单的去做。就是在html中布局一样可以使用表格定位。想把一个表单元素加入cell,要借助cell的setCellEvent方法。以一个checkbox为例。新建一个类CheckBoxForm,实现PdfPCellEvent接口。需要实现一个cellLayout的方法。
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
position可以好好利用,它包含当前cell的位置信息,你可以用它来确定自己checkbox的位置。
position.top()-position.bottom()就能得到高position.right()-position.left()可以得到长,如果需要这两个值得花可以如此计算。下面的代码就是定义一个宽度为a的checkbox的rectangle 。它在cell中水平居中,垂直也居中。

    float bo = (position.top()-position.bottom()-a)/2; 
    float ao = (position.right()-position.left()-a)/2;     
    Rectangle rectangle = new Rectangle(position.left() + ao, position 
          .bottom() + bo, position.left() +ao+ a, position.bottom()+ bo + a);

然后把它加入Document
    RadioCheckField tf = new RadioCheckField(writer, rectangle, fieldname, 
          "f"); 
    tf.setCheckType(RadioCheckField.TYPE_SQUARE); 
    tf.setBorderWidth(1); 
    tf.setBorderColor(Color.black); 
    tf.setBackgroundColor(Color.white);       
    try { 
        PdfFormField field = tf.getCheckField();         
        writer.addAnnotation(field); 
    } catch (IOException e) { 
        e.printStackTrace(); 
    } catch (DocumentException e) { 
        e.printStackTrace(); 
    }

其它的元素与此类似。
4 PdfPTable和Table

说不上哪种更好用,有时候不能不使用PdfPTable。可惜它只有setColspan方法,没有setRowspan。嵌套的时候也有区别,PdfPTable是用addcell()加入嵌套表的,table则有一个更明了的方法insertTable()。PdfPTable想进行设置border之类的操作要先获得一个默认cell,
pdfPTableName.getDefaultCell().setBorder(Rectangle.NO_BORDER);//设置无框的表
另外在PdfPTable中,一些修饰属性会因为设置的时机不正确而没有效果。如,适用cell的构造函数加入了文本,在cell的setVerticalAlignment()fangfa去设定垂直对齐方式就不会有效。还有一个有意思的不同是table默认外边框是加粗的,而PdfPTable则一样粗细。

5 字体

iText的例子有很多足够用,给出一些pdf的字体名称和编码,如果想使用内嵌字体的话。
语言 PDF 字体名
简体中文 STSong-Light
繁体中文 MHei-Medium
MSung-Light
日语 HeiseiKakuGo-W5
HeiseiMin-W3
韩语 HYGoThic-Medium
HYSMyeongJo-Medium

字符集 编码
简体中文 UniGB-UCS2-H
UniGB-UCS2-V
繁体中文 UniCNS-UCS2-H
UniCNS-UCS2-V
日语 UniJIS-UCS2-H
UniJIS-UCS2-V
UniJIS-UCS2-HW-H
UniJIS-UCS2-HW-V
韩语 UniKS-UCS2-H
UniKS-UCS2-H
必须要有Asian的包才可以用,也可以使用TrueType字体。

Itext PDF生成 加水印图片文字

由于项目中用到,所以网上找了很多,但或多或少有些问题,我整理更新了一下。
Itext架包下载地址:
http://download.csdn.net/detail/qm4050/5531349

PDF生成代码如下:

import java.io.*;  
import com.itextpdf.text.*;  
import com.itextpdf.text.pdf.*;  
 
/** 
 * PDF生成 
 */  
public class PDFBuild {  
 
    public static void buidPDF(String pdfFile, String imageFile,  
            String waterMarkName, int permission) {  
        try {  
            File file = File.createTempFile("tempFile", ".pdf"); // 创建临时文件  
 
            // 生成PDF  
            if (createPDFFile(file)) {  
                waterMark(file.getPath(), imageFile, pdfFile, waterMarkName,  
                        permission); // 添加水印  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
 
    /** 
     * 创建PDF文件 
     *  
     * @param file 
     *            临时文件 
     * @return 成功/失败 
     */  
    public static boolean createPDFFile(File file) {  
        Rectangle rect = new Rectangle(PageSize.A4);  
        Document doc = new Document(rect, 50.0F, 50.0F, 50.0F, 50.0F);  
        try {  
            PdfWriter.getInstance(doc, new FileOutputStream(file));  
            doc.open();  
 
            BaseFont bf = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1",  
                    "Identity-H", true);// 使用系统字体  
 
            Font font = new Font(bf, 14.0F, 0);  
            font.setStyle(37); // 设置样式  
            font.setFamily("宋体"); // 设置字体  
 
            Paragraph p = new Paragraph("付 款 通 知 书\r\n", font);  
            p.setAlignment(1);  
            doc.add(p);  
            doc.close();  
            return true;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return false;  
    }  
 
    public static void waterMark(String inputFile, String imageFile,  
            String outputFile, String waterMarkName, int permission) {  
        try {  
            PdfReader reader = new PdfReader(inputFile);  
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(  
                    outputFile));  
 
            BaseFont base = BaseFont.createFont(  
                    "C:/WINDOWS/Fonts/SIMSUN.TTC,1", "Identity-H", true);// 使用系统字体  
 
            int total = reader.getNumberOfPages() + 1;  
            Image image = Image.getInstance(imageFile);  
 
            // 图片位置  
            image.setAbsolutePosition(400, 480);  
            PdfContentByte under;  
            int j = waterMarkName.length();  
            char c = 0;  
            int rise = 0;  
            for (int i = 1; i < total; i++) {  
                rise = 400;  
                under = stamper.getUnderContent(i);  
                under.beginText();  
                under.setFontAndSize(base, 30);  
 
                if (j >= 15) {  
                    under.setTextMatrix(200, 120);  
                    for (int k = 0; k < j; k++) {  
                        under.setTextRise(rise);  
                        c = waterMarkName.charAt(k);  
                        under.showText(c + "");  
                    }  
                } else {  
                    under.setTextMatrix(240, 100);  
                    for (int k = 0; k < j; k++) {  
                        under.setTextRise(rise);  
                        c = waterMarkName.charAt(k);  
                        under.showText(c + "");  
                        rise -= 18;  
 
                    }  
                }  
 
                // 添加水印文字  
                under.endText();  
 
                // 添加水印图片  
                under.addImage(image);  
 
                // 画个圈  
                under.ellipse(250, 450, 350, 550);  
                under.setLineWidth(1f);  
                under.stroke();  
            }  
            stamper.close();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
 
    public static void main(String[] args) {  
        String imageFilePath = "D:/itext2.png"; // 水印图片路径  
        String pdfFilePath = "D:/itext.pdf"; // 文件生成路径  
        buidPDF(pdfFilePath, imageFilePath, "正版授权", 16);  
    }  
}

http://blog.csdn.net/qm4050/article/details/9039069

iText PDF template

iTextPDFtemplatePdfStamper
useful materials
http://svn.assembla.com/svn/jarbilling/billing/trunk/billing/vendor/pdfmanipulator/Fdf.java
http://svn.assembla.com/svn/jarbilling/billing/trunk/billing/vendor/pdfmanipulator/test/BatchFormFill.java
http://www.coderanch.com/t/63861/open-source/iText-Silent-Print
http://threebit.net/mail-archive/itext-questions/msg02068.html

part4.chapter13.PrintPreferencesExample:
http://itextpdf.com/examples/iia.php?id=230

http://comments.gmane.org/gmane.comp.java.lib.itext.general/28053

import java.io.ByteArrayOutputStream;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.util.Map.Entry;  
import java.util.Set;  

import com.itextpdf.text.Document;  
import com.itextpdf.text.FontFactory;  
import com.itextpdf.text.PageSize;  
import com.itextpdf.text.pdf.AcroFields;  
import com.itextpdf.text.pdf.AcroFields.Item;  
import com.itextpdf.text.pdf.BaseFont;  
import com.itextpdf.text.pdf.PdfReader;  
import com.itextpdf.text.pdf.PdfStamper;  

public class PdfTest2 {  
    public static void main(String[] args) throws Exception {  
        // Create output PDF  
        Document document = new Document(PageSize.A4);  

        document.open();  

        // Load existing PDF  
        PdfReader reader = new PdfReader(new FileInputStream(  
                "Sample application form_ENG edit.pdf"));  
        // PdfImportedPage page = writer.getImportedPage(reader, 1);  

        // we'll create the file in memory  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        // Now we create the PDF  
        PdfStamper stamper = new PdfStamper(reader, baos);  
        // We alter the fields of the existing PDF  
        AcroFields fields = stamper.getAcroFields();  

         BaseFont baseFont = FontFactory.getFont(FontFactory.TIMES_ITALIC,BaseFont.IDENTITY_H, new Float(12)).getBaseFont();  

        Set<Entry<String, Item>> entrySet = fields.getFields().entrySet();  
        for (Entry<String, Item> entry : entrySet) {  
            String key = entry.getKey();  
            System.out.println(key);  
            fields.setField(key, key);  
//          if (fields.getFieldType(key) == AcroFields.FIELD_TYPE_TEXT) {  
                fields.setFieldProperty(key, "textfont", baseFont, null);  
                fields.setFieldProperty(key, "textsize", new Float(16), null);  
//          }  
        }  
        fields.setField("tradeID", "007");  
        // close the stamper  
        fields.setField("acNo", "9527");  

        stamper.close();  

        document.close();  

        // let's write the file in memory to a file anyway  
        FileOutputStream fos = new FileOutputStream("HelloWorld-Gavin.pdf");  
        fos.write(baos.toByteArray());  
        fos.close();  

    }  
}

http://cute-spring.iteye.com/blog/1608243

iText 操作Pdf 简单整理

javaITextPdf
iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
下面简单的整理几个操作pdf相关实例。

1.PDF模版填充

首先需要有一个pdf文件作为模版,可以用如adobe acrobat等工具,添加几个文本域在pdf中,下面就是模版完成后的部分截图。

/** 
 * @throws IOException 
 * @throws DocumentException 
 */  
@SuppressWarnings("unchecked")  
@Test  
public void fillTemplate()  
        throws IOException, DocumentException {  
    PdfReader reader = new PdfReader(templateFile); // 模版文件目录  
    PdfStamper ps = new PdfStamper(reader, new FileOutputStream(  
            "f:/fillTemplate.pdf")); // 生成的输出流  
    // ByteArrayOutputStream bos = new ByteArrayOutputStream();  
    // PdfStamper ps = new PdfStamper(reader, bos);  

    AcroFields s = ps.getAcroFields();  

    Map<String, Object> fieldMap = s.getFields(); // pdf表单相关信息展示  
    for (Map.Entry<String, Object> entry : fieldMap.entrySet()) {  
        String name = entry.getKey(); // name就是pdf模版中各个文本域的名字  
        Item item = (Item) entry.getValue();  
        System.out.println("[name]:" + name + ", [value]: " + item);  
    }  

    s.setField("CUSTOMERNAME", "as该多好公司");  
    s.setField("TEL", "123456asdzxc");  
    s.setField("CONTACT", "我是联系人123");  

    ps.setFormFlattening(true); // 这句不能少  
    ps.close();  
    reader.close();  
}

效果如下所示:

[name]:CUSTOMERNAME, [value]: com.lowagie.text.pdf.AcroFields$Item@1690726
[name]:CONTACT, [value]: com.lowagie.text.pdf.AcroFields$Item@5483cd
[name]:TEL, [value]: com.lowagie.text.pdf.AcroFields$Item@1befab0

输出的pdf文件内容:

注:默认情况下如果填充的字段值含中文时,将提示报错,这是因为没有IText相应的中文语言包,需要下载亚洲语言包,ITextAsian.jar。

如不想生成文件流(临时文件),可用ByteArrayOutputStream代替。

2.多个PDF合并
/** 
 * 多个PDF合并功能 
 *  
 * @param files 
 *            多个PDF的文件路径 
 * @param os 
 *            生成的输出流 
 */  
public static void mergePdfFiles(String[] files, OutputStream os) {  
    try {  
        Document document = new Document(  
                new PdfReader(files[0]).getPageSize(1));  
        PdfCopy copy = new PdfCopy(document, os);  
        document.open();  
        for (int i = 0; i < files.length; i++) {  
            PdfReader reader = new PdfReader(files[i]);  
            int n = reader.getNumberOfPages();  
            for (int j = 1; j <= n; j++) {  
                document.newPage();  
                PdfImportedPage page = copy.getImportedPage(reader, j);  
                copy.addPage(page);  
            }  
        }  
        document.close();  
    } catch (IOException e) {  
        e.printStackTrace();  
    } catch (DocumentException e) {  
        e.printStackTrace();  
    }  
}

如果想要直接合并输出流,可以换成输出流的集合

/** 
 * 多个PDF合并功能 
 *  
 * @param osList 
 * @param os 
 */  
public static void mergePdfFiles(List<ByteArrayOutputStream> osList,  
        OutputStream os) {  
    try {  
        Document document = new Document(new PdfReader(osList.get(0)  
                .toByteArray()).getPageSize(1));  
        PdfCopy copy = new PdfCopy(document, os);  
        document.open();  
        for (int i = 0; i < osList.size(); i++) {  
            PdfReader reader = new PdfReader(osList.get(i).toByteArray());  
            int n = reader.getNumberOfPages();  
            for (int j = 1; j <= n; j++) {  
                document.newPage();  
                PdfImportedPage page = copy.getImportedPage(reader, j);  
                copy.addPage(page);  
            }  
        }  
        document.close();  
    } catch (IOException e) {  
        e.printStackTrace();  
    } catch (DocumentException e) {  
        e.printStackTrace();  
    }  
}

3.PDF分割

/** 
 * 单个Pdf文件分割成N个文件 
 *  
 * @param filepath 
 * @param N 
 */  
public static void partitionPdfFile(String filepath, int N) {  
    Document document = null;  
    PdfCopy copy = null;  
 
    try {  
        PdfReader reader = new PdfReader(filepath);  
        int n = reader.getNumberOfPages();  
        if (n < N) {  
            System.out.println("The document does not have " + N  
                    + " pages to partition !");  
            return;  
        }  
        int size = n / N;  
        String staticpath = filepath.substring(0,  
                filepath.lastIndexOf("\\") + 1);  
        String savepath = null;  
        List<String> savepaths = new ArrayList<String>();  
        for (int i = 1; i <= N; i++) {  
            if (i < 10) {  
                savepath = filepath.substring(  
                        filepath.lastIndexOf("\\") + 1,  
                        filepath.length() - 4);  
                savepath = staticpath + savepath + "0" + i + ".pdf";  
                savepaths.add(savepath);  
            } else {  
                savepath = filepath.substring(  
                        filepath.lastIndexOf("\\") + 1,  
                        filepath.length() - 4);  
                savepath = staticpath + savepath + i + ".pdf";  
                savepaths.add(savepath);  
            }  
        }  
 
        for (int i = 0; i < N - 1; i++) {  
            document = new Document(reader.getPageSize(1));  
            copy = new PdfCopy(document, new FileOutputStream(  
                    savepaths.get(i)));  
            document.open();  
            for (int j = size * i + 1; j <= size * (i + 1); j++) {  
                document.newPage();  
                PdfImportedPage page = copy.getImportedPage(reader, j);  
                copy.addPage(page);  
            }  
            document.close();  
        }  
 
        document = new Document(reader.getPageSize(1));  
        copy = new PdfCopy(document, new FileOutputStream(  
                savepaths.get(N - 1)));  
        document.open();  
        for (int j = size * (N - 1) + 1; j <= n; j++) {  
            document.newPage();  
            PdfImportedPage page = copy.getImportedPage(reader, j);  
            copy.addPage(page);  
        }  
        document.close();  
 
    } catch (IOException e) {  
        e.printStackTrace();  
    } catch (DocumentException e) {  
        e.printStackTrace();  
    }  
}

4.在Servlet中调用IText返回给客户端

a.将文件流直接返回给客户端

@Override  
protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException {  
    resp.setContentType("application/pdf");  
    ServletOutputStream sos = resp.getOutputStream();  
    FileInputStream in = new FileInputStream("f:/fillTemplate.pdf");  
    byte data[] = new byte[1024];  
 
    int len = 0;  
    while ((len = in.read(data)) != -1) {  
        sos.write(data, 0, len);  
    }  
 
    sos.flush();  
    in.close();  
    sos.close();  
}

b.填充模版文件后将输出流返回给客户端

@Override  
protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
        throws ServletException, IOException {  
    resp.setContentType("application/pdf");  
 
    ByteArrayOutputStream baos = new ByteArrayOutputStream();  
    PdfReader reader = null;  
    PdfStamper ps = null;  
    try {  
        reader = new PdfReader(""); // 模版文件目录  
        ps = new PdfStamper(reader, baos);  
        AcroFields s = ps.getAcroFields();  
        s.setField("CUSTOMERNAME", "as该多好公司");  
        s.setField("TEL", "123456asdzxc");  
        s.setField("CONTACT", "我是联系人123");  
 
        ps.setFormFlattening(true); // 这句不能少  
        ps.close();  
        reader.close();  
    } catch (DocumentException e) {  
        e.printStackTrace();  
    }  
 
    ServletOutputStream sos = resp.getOutputStream();  
    baos.writeTo(sos);  
    sos.flush();  
    sos.close();  
}

http://zhuchengzzcc.iteye.com/blog/1603671


本页面的文字允许在知识共享 署名-相同方式共享 3.0协议和GNU自由文档许可证下修改和再使用,仅有一个特殊要求,请用链接方式注明文章引用出处及作者。请协助维护作者合法权益。


系列文章

文章列表

  • Java PDF Form报表表单填写赋值

这篇文章对你有帮助吗,投个票吧?

rating: 0+x

留下你的评论

Add a New Comment