Itext输出pdf文件
一、经典的“五步”:
利用iText五步创建一个PDF文件:helloword。
第一步,创建一个 itextpdf.text.Document对象的实例:
Document document = new Document();
第二步,为该Document创建一个Writer实例:
PdfWriter.getInstance(document, new FileStream("Chap0101.pdf",
FileMode.Create));
第三步,打开当前Document:
document.Open();
第四步,为当前Document添加内容:
document.Add(new Paragraph("Hello World"));
第五步,关闭Document
document.Close();
在例中,不难看出,制作一个PDF文件是非常简单的。
二、详细一点:
第一步,创建一个Document对象实例:
Document对象的构造方法有三种:
1.public Document();
2.public Document(Rectangle pageSize);
3.public Document(Rectangle pageSize, int marginLeft,
int marginRight,int marginTop,int marginBottom);
//marginLeft,marginRight,marginTop,marginBottom分别对应左右上下页边距
第一个构造函数以A4页面作为参数调用第二个构造函数,第二个构造函数以每边3磅页边距为参数调用第三个构造函数。
通常我们只要用第一个或第二个就可以了,如:
Document document = new Document(PageSize.A4);
//如果希望使用横向页面,你只须使用rotate()方法
document = new Document(PageSize.A4.rotate());
当然,你也可以自己定义一个纸张页面,如下面的例子:
//创建一个1027*768大小的浅黄色背景的页面
Rectangle pageSize = new Rectangle(1024,768);
pageSize.BackgroundColor = new Color(0xff,0xff,0xde);
Document document = new Document(pageSize);
第二步,创建Writer实例:
你可以通过下面的方法创建一个实例:
PdfWriter writer = PdfWriter.getInstance(document,
new FileStream("Chap01xx.pdf"));
其中,第二个参数可以是任何一种流,你可以写入文件中或者直接输出到servlet等等。一个document可以创建多个writer实例,你可以用多个writer实例来输出不同的pdf。
第三步,打开document:
这个地方没什么说的,一句话:
document.open();
但是请注意:有些设置是在document打开之前添加的,如页面效果等。
第四步,添加内容:
这一步是整个过程的最大的部分,接下来的内容基本都是在此范围内的。具体内容请查看后面几章,在这里,我想说的是,若果你创建了多个writer实例,并且你希望这几个writer 实例输出的pdf文件有些许不同的地方。请参考下面例子的部分代码:
//创建两个writer实例
PdfWriter writerA = PdfWriter.getInstance(document,new
FileStream("writerA.pdf", FileMode.Create));
PdfWriter writerB = PdfWriter.getInstance(document,new
FileStream("writerB.pdf", FileMode.Create));
document.open();
//writer A不添加下面这段话
writerA.pause();
document.add(new Paragraph("这段文字不会显示在writerA输出的pdf文件
writerA.pdf中"));
//writer A添加下面这段话
writerA.resume();
document.add(new Paragraph("这段文字会显示在writerA输出的pdf文件
writerB.pdf中"));
第五步,关闭document
关闭document 非常重要, 因为它将关闭正在运行的Writer并将内容写入文件,该方法在最后被调用,你应该总是要关闭文档。
document.close();
至此,itext可以成功创建一个pdf文件了。下面是一个itext生成pdf文件的示例,里面出
现的一些类和方法将在后面讲到:
import java.io.FileOutputStream;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
public class ItextTest {
public static void main(String[] args) {
System.out.println("The first Pdf");
/**第一步,创建一个Document对象*/
Document document = new Document(PageSize.A4);
try{
/**第二步,创建一个writer实例*/
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("D:\\theFirstPdf.pdf"));
/**第三步,打开document*/
document.open();
/**第四步,添加内容*/
//创建一个有3列的表格
PdfPTable table = new PdfPTable(3);
//创建一个段落
Paragraph para = new Paragraph("header with colspan 3");
//定义一个表格单元,并将段落放入表格单元中
PdfPCell cell = new PdfPCell(para);
//设置表格单元的列跨度
cell.setColspan(3);
table.addCell(cell);
//把下面的9项依次加入到表格中,当一行填满自动填到下一行中
table.addCell("1.1");
table.addCell("1.2");
table.addCell("1.3");
table.addCell("2.1");
table.addCell("2.2");
table.addCell("2.3");
table.addCell("3.1");
table.addCell("3.2");
table.addCell("3.3");
//重新定义单元格
cell = new PdfPCell(new Paragraph("cell test1"));
//定义单元格的边框颜色
cell.setBorderColor(BaseColor.RED);
//把单元格添加到表格中
table.addCell(cell);
//重新定义单元格
cell = new PdfPCell(new Paragraph("cell test2"));
//定义单元格的背景颜色
cell.setBackgroundColor(BaseColor.BLUE);
//把单元格添加到表格中
table.addCell(cell);
//把表格添加到文档中
document.add(table);
}catch(Exception e){
e.printStackTrace();
}
/**第五步,关闭document*/
document.close();
}
}
第二章块、短句和段落
一、块
块(Chunk)是能被添加到文档的文本的最小单位,块可以用于构建其他基础元素如短句、段落、锚点等,块是一个有确定字体的字符串,要添加块到文档中时,其他所有布局变量均要被定义。下面一行中,我们创建了一个内容为“hello World”、红色、斜体、COURIER字体、尺寸20的一个块:
Chunk chunk = new Chunk("hello world",
FontFactory.getFont(FontFactory.COURIER,20,
Font.ITALIC,BaseColor.RED));
二、短句
短句(Phrases)是一系列以特定间距(两行之间的距离)作为参数的块,一个短句有一个主字体,但短句中的一些块具有不同于主字体的字体,你有更多的选择去创建短句。短句的创建方式和块类似:
Phrase ph = new Phrase(new Chunk("hello world",
FontFactory.getFont(FontFactory.COURIER,20,
Font.ITALIC,BaseColor.RED)));
三、段落
段落是一系列块和(或)短句。同短句一样,段落有确定的间距。用户还可以指定缩排;在边和(或)右边保留一定空白,段落可以左对齐、右对齐和居中对齐。添加到文档中的每一个段落将自动另起一行。有几种办法建立一个段落,如:
Paragraph p1 = new Paragraph(new Chunk("This is my first paragraph.",
FontFactory.getFont(FontFactory.COURIER, 12))); Paragraph p2 = new Paragraph(new Phrase("This is my second paragraph.",
FontFactory.getFont(FontFactory.COURIER, 12))); Paragraph p3 = new Paragraph("This is my third paragraph.",
FontFactory.getFont(FontFactory.COURIER, 12));
段落还有一个add方法,用来添加内容,下面的对象将会被添加到p1中去:
p1.add("you can add String");
p1.add(new Chunk("you can add Chunk"));
p1.add(new Phrase("you can add Phrase"));
说明:一个段落有一个且仅有一个间距,如果你添加了一个不同字体的短句或块,原来的间距仍然有效,你可以通过SetLeading来改变间距,但是段落中所有内容将使用新的中的间距。
第三章锚点、列表
一、锚点
我们都知道HTML中的超文本链接,当我们点击某些语句,你能够跳转到网上的其他页。在PDF中也可以实现这种功能。事实上,在第十一章整个章节中有关于PDF链接的介绍,但这是iText的更高级的应用,本章中我们处理简单的iText。
如果你想在文档中添加一个外部链接(例如使用URL链接到WEB上的其他文档),你可以简单地使用Anchor对象,它派生于Phrase对象,使用方法相同。只有两种额外方法定义两种额外变量:setName和setReference。
外部链接示例:
Anchor anchor = new Anchor("website",
FontFactory.getFont(FontFactory.COURIER, 12,
Font.UNDERLINE, new BaseColor(0, 0, 255)));
anchor.setReference("https://www.wendangku.net/doc/1a12992409.html,");
anchor.setName("website");
如果你想添加内部链接,你需要选择该链接不同的名称,就象你相位在HTML中利用名称作为锚点一样。为达到该目的,你需要添加一个“#”。
内部链接示例:
Anchor anchor1 = new Anchor("This is an internal link");
anchor1.setName("link1");
Anchor anchor2 = new Anchor("Click here to jump to the internal link"); anchor2.setReference("#link1");
二、列表
通过类List 和ListItem,你可以添加列表到PDF文件中,对于列表你还可以选择是否排序。排序列表示例:
List list = new List(true, 20);
list.add(new ListItem("First line"));
list.add(new ListItem("The second line"));
list.add(new ListItem("Third line"));
输出结果如下:
1.First line
2.The second line
3.Third line
不排序列表示例:
List overview = new List(false, 10);
overview.add(new ListItem("This is an item"));
overview.add("This is another item");
输出结果如下:
?This is an item
?This is another item
你可以通过setListSymbol方法更改列表符号:
// 用字符串作为列表符号
list.setListSymbol("*");
// 用Chunk 作为列表符号(包含“?”字符)
list.setListSymbol(new Chunk("\u2022"
,FontFactory.getFont(FontFactory.COURIER, 20)));
//用图片作为列表符号
list.setListSymbol(new Chunk(Image.getInstance("myBullet.gif"), 0, 0));
还可以使用setIndentationLeft和setIndentationRight方法设置缩排,列表符号的缩排在构造函数中设置。
第四章绘图对象
因为工作需要,通常我们都需要在pdf中添加一些简单的图形,如直线,斜线,矩形等。如果你想做出更为复杂的图形,请参照相关api或直接登录itext的官方网站寻求帮助(英文的),本文不作赘述(好吧我承认其实我也不会……)。
如果你只需要有限的功能,你可以使用Graphic对象:
Graphic grx = new Graphic();
//添加一个矩形
grx.rectangle(100, 700, 100, 100);
// 添加一条斜线
grx.moveTo(100, 700);
grx.lineTo(200, 800);
// 将图形显示出来
grx.stroke();
document.Add(grx);
当你想给页面加一个边框或者在文本当前位置画一条水平线时,图形对象非常有用。下面的方法用指定的宽度、间距(如果需要)和颜色画一个边框。
public void setBorder(float linewidth, float extraSpace);
public void setBorder(float linewidth, float extraSpace, BaseColor color);
下面的方法用指定的宽度(如果需要)和颜色画一条水平线,线的长度是指定两边缘间可用面积的的百分比。
public void setHorizontalLine(float linewidth, float percentage);
public void setHorizontalLine(float linewidth, float percentage, BaseColor color);
第五章表格
在web开发中,如果需要生成章程、文件之类的pdf文件,表格通常是必不可少的。当然,表格通常也是最繁杂的部分,你需要消耗大量的时间来调整表格的样式问题。
重点:如果你仅仅生成PDF文件(没有XML、HTML、RTF……),使用类pdfPTable代替类Table更好。本文主要针对的是输出pdf文件,因此使用pdfPtable类来做说明,如果要使用Table类请百度。
创建一个表格的方法如下:
//创建一个有3列的表格
PdfPTable table = new PdfPTable(3);
//创建一个4列,宽度比例为1:2:3:4的表格
float[] widthP = {0.1f,0.2f,0.3f,0.4f};
PdfPTable table1 = new PdfPTable(widthP);
//添加一个单元格
PdfPCell cell = new PdfPCell(new Chunk("a table again"));
通常只是通过这样的方式创建的表格可能会很难看,所以需要调用一些方法来修饰它。
通过document.add()方法添加的PdfPTable对象,其默认宽度是页面可编辑空间的80%并居中对齐。要想改变这些默认值,可使用setWidthPercentage()和setHorizontalAlignment()
方法。
Cell的方法可参考下面代码:
//设置单元格最小高度
cell.setMinimumHeight(20f);
//设置单元格对齐方式
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
//设置单元格跨两列
cell.setColspan(2);
//设置单元格背景颜色
cell.setBackgroundColor(BaseColor.RED);
//设置单元格边框(0为无边框)
cell.setBorder(0);
//设置单元格边框的颜色
cell.setBorderColor(BaseColor.RED);
//更多的方法设置请参考api或自己动手试验
第六章图像
一、Image对象
毫不例外,有时候你得在你的pdf文件中加入图片,你可以通过itext提供的Image对象来将图片放进去。
Image是一个抽象类,故得到实例的方法将判断给出的图片的类别(GIF、Jpeg、PNG……)并返回对象的类别Gif、Jpeg、Png……,一些图片会被忽略,如果你想知道哪些图片会被忽略,请查阅FAQ(https://www.wendangku.net/doc/1a12992409.html,/iText/faq.html#images)。
创建一个Image实例有很多种方法,下面列举了一些常用的方式:
public static Image getInstance(byte[] bytes);
public static Image getInstance(Image img);
public static Image getInstance(URL url);
public static Image getInstance(string filename);
二、图片的位置
?对齐方式
通过下面方法设置图片的对齐方式:
image.setAlignment(Image.LEFT);
image.setAlignment(Image.RIGHT);
image.setAlignment(Image.MIDDLE);
?图片和文本
另外,你还可以指定文本相对图片的环绕方式:
Image.TEXTWRAP Image.UNDERLYING
?绝对位置
当制作PDF文件时,你可能用到该方法:
public void setAbsolutePosition(int absoluteX, int absoluteY)
三、缩放和旋转图片
?缩放
有几种办法可以缩放图片:
public void scaleAbsolute(int newWidth, int newHeight)
public void scalePercent(int percent)
public void scalePercent(int percentX, int percentY)
public void scaleToFit(int fitWidth, int fitHeight)
如果一张图片大小为194×202象素,如果你想让图片小一些,你可以通过scaleAbsolute(97, 101)进行缩放,使用scalePercent(50)也能到达同样的效果。还可以通过scaleAbsolute(194, 101)来减小。
?对分辨率的影响
如果一个图片不经任何缩放,其分辨率(resolution)为72,如果该图片缩放比例为50%,则分辨率为144,如果有更低的缩放比,则分辨率将更大,因为象素相同但尺寸变得更小了。使用72/300=24%的比例放置一个300dpi的图片,例:你用300dpi扫描了一个5×5英寸的图片,图片结果为1500×1500象素(5×300),当你用24%(72/300=0.24)的比例将该图片放置到PDF文件中时,PDF中的图片将为5×5英寸1500X1500象素300dpi,该图片将始终为1500X1500象素而不管尺寸如何。
?旋转
可以通过下面的方法旋转图片
public void setRotation(double r)
掌握了上面的内容,基本上相当一部分的pdf你都可以通过itext生成出来了,接下来,介绍的是一些让人有些纠结的东西。
字体
Itext的开发者起初并没有考虑到亚洲语言的字体显示问题,所以你会发现输入中文时并不能显示出来,要显示中文,必须引入itext-asian.jar这个包。
Windows中一般都是使用TrueType字体,每个中文版Windows操作系统均默认安装了宋体、仿宋、黑体和楷体四种字体,你还可以安装其他第三方字体,如安装了Office 2000后,会自动安装华文行楷等字体,比较奇怪的是,在PDF文件中插入了一种本计算机才有的字体,在打开PDF文件的计算机上虽然没有该字体,但仍然能正常显示!这有别于Word 文件,Word文件将当前计算机中没有的字体一律用宋体代替,这大概是意外收获吧。
字体文件一般保存在windir\Fonts目录中,扩展名为TTF,还有扩展名为TTC的字体文件,也是TrueType字体,不过是一个集合,也就是里面有多种字体。下面列出windows2000简体中文版四种标准字体的文件名称:
SIMSUN.TTC:宋体和新宋体
SIMKAI.TTF:楷体
SIMHEI.TTF:黑体
SIMFANG.TTF:仿宋体
你可以通过下面的方式定义一个字体:
//创建宋体
BaseFont simSun = BaseFont.createFont("C:\\windows\\fonts\\simsun.ttc,1"
,BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
//创建黑体(如果上面方法创建黑体失败的话,不妨把1去掉)
BaseFont simHei = BaseFont.createFont("C:\\windows\\fonts\\simhei.ttf"
,BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
//创建文档标题字体:黑体,26,加粗
Font titleFont = new Font(simHei,26,Font.BOLD);
//创建正文字体:宋体,11
Font articleFont = new Font(simSun,11,Font.NORMAL);
水印
可能你的项目要求你的pdf文件具有防伪的功能,通常需要你添加水印,在itext中添加水印主要有两种方法:
1.使用Watermark类(低版本的itext):
Watermark watermark = new Watermark(.......);
writer.add(watermark);
2.使用PdfContentByte类
PdfContentByte under = writer.getDirectContentUnder();
Watermark类我并清楚所以不再说明,具体可以百度搜索一下,当然,如果你对此比较有心得,你可以在此处完善它,先在此感谢你的无私付出!
此处预留
1.通常情况下,使用pdfContentByte类在pdf中添加水印,你可以参照下面的示例:
PdfContentByte under = writer.getDirectContentUnder();
....
document.newPage();
//添加水印图片
under.addImage(image);
//添加水印文字(斜向上)
String waterMarkName = "water mark";
under.beginText();
under.setColorFill(BaseColor.LIGHT_GRAY);
under.setFontAndSize(bfTitle, 100);
under.setTextMatrix(70, 0);
int rise = 200;
for (int k = 0; k under.setTextRise(rise); char c = waterMarkName.charAt(k); under.showText(c + " "); rise += 100; } under.endText(); 优点:当document新增一页后,我们可以自行选择是否添加水印 缺点:当pdf的文件页数不确定时,我们不能很好的控制哪一页有水印哪一页没有水印。 2.第二种方法你可以很轻松的在网上找到: /**InPdfFile 要加水印的原pdf文件路径 outPdfFile 加了水印后要输出的路径markImagePath 水印图片路径*/ PdfReader reader = new PdfReader(InPdfFile, "PDF".getBytes()); int pageSize = reader.getNumberOfPages() + 1; //文档页数 PdfStamper stamp = new PdfStamper(reader , new FileOutputStream(outPdfFile)); Image img = Image.getInstance(markImagePath);// 插入水印 img.setAbsolutePosition(150, 100); for(int i = 1; i <= pageSize; i++) { PdfContentByte under = stamp.getUnderContent(i); under.addImage(img); } stamp.close();// 关闭 File tempfile = new File(InPdfFile); if(tempfile.exists()) { tempfile.delete(); } 优点:可以很轻松的添加水印 缺点:当你的pdf文件是在web程序中直接生成并且作为流的方式提供给用户下载时,此 种方法会添加服务器的负担。 3.第三种方法是使用页面效果,具体示例如下: /** *创建一个类继承PdfPageEventHelper类 */ public class MyPdfEvent extends PdfPageEventHelper{ public Image getImage() { return image; } public void setImage(Image image) { this.image = image; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } public BaseFont getFont() { return font; } public void setFont(BaseFont font) { this.font = font; } private Image image; private String str; private BaseFont font; public MyPdfEvent(Image waterMarkImage){ image = waterMarkImage; } public MyPdfEvent(String waterMarkString,BaseFont baseFont){ str = waterMarkString; font = baseFont; } public MyPdfEvent(Image waterMarkImage,String waterMarkString,BaseFont baseFont){ image = waterMarkImage; str = waterMarkString; font = baseFont; } @Override public void onStartPage(PdfWriter writer, Document document) { try{ PdfContentByte under = writer.getDirectContentUnder(); PdfContentByte under = writer.getDirectContentUnder(); if(image!=null){ under.addImage(image); } if(str!=null){ BaseFont waterMarkTitle = font; under.beginText(); under.setColorFill(BaseColor.LIGHT_GRAY); under.setFontAndSize(waterMarkTitle, 100); under.setTextMatrix(70, 0); int rise = 200; for (int k = 0; k under.setTextRise(rise); char c = str.charAt(k); under.showText(c + " "); rise += 100; } under.endText(); } }catch(Exception e){ e.printStackTrace(); } } } //在writer中添加页面效果 writer.setPageEvent(new MyPdfEvent(waterMarkImage)); document.open();//在document打开前 优点:没有文档页数不确定时有的页面有水印有的页面没水印的问题,可以直接流输出到用户的电脑里。 缺点:不能使有的页面不显示水印,要么全显示水印,要不全不显示水印。 当然,我所说的只是我理解的一部分,相信还有其他更灵活更有效的方法,如果你有更为有效的方法,请在后面补充,先谢谢你的贡献。 组合使用 通常我们开发程序所使用的工具并不仅限于一种,前台使用一种技术,后台使用另一种技术,有时候可能后台所使用的工具需要处理前台工具生成的特有的数据,这就难免会产生各种各样稀奇古怪的问题,需要程序员做特殊处理,所以在本文的最后,附上一个我本人和同事针对开发过程中遇到的问题所想出来的解决办法,旨在起到抛砖引玉的作用。“赠人玫瑰,手留余香”,如果你在使用itext过程中遇到了此类问题,并且运用你的智慧想出了解决办法,希望你能将你的代码贴出来,以供他人参考。谢谢! /** * @author jiangchao cuishiqiang * 将fck存储在数据库中的数据通过jsoup转换成itext * 能识别的对象 */ public class FckToPdf { /** * 将fck所存的内容转换成itext能识别的对象 * @param proName 项目名称,用来命名fck中复制粘贴的图像的名称 * @param requestRealPath 项目路径, request.getSession().getServletContext().getRealPath("/") * @param fckStr 待转换的fck内容 * @param font itext字体 * @return itext Phrase对象 * @throws IOException */ public static Phrase decode(String proName,String requestRealPath,String fckStr,Font font) throws IOException{ Phrase ph = new Phrase(); StyleSheet st = new StyleSheet(); st.loadTagStyle("body", "leading", "16,0"); FontFactory.register(requestRealPath+"simsun.ttc"); org.jsoup.nodes.Document jsdoc = Jsoup.parse(fckStr); Elements spans = jsdoc.select("span"); for(org.jsoup.nodes.Element span : spans){ fckStr = fckStr.replace(span.attr("style"),""); } Elements links = jsdoc.select("img"); int xl = 0; for(org.jsoup.nodes.Element link:links){ if(link.attr("src").startsWith("http:")){ }else if(link.attr("src").startsWith("data:")){ String imgStr = link.attr("src").substring(22); BASE64Decoder decoder = new BASE64Decoder(); if(imgStr==null) continue; try{ byte[] bytes = decoder.decodeBuffer(imgStr); for(int i=0;i { if(bytes[i]<0) {//调整异常数据 bytes[i]+=256; } } // 生成jpeg图片 String filePath = requestRealPath+"userfiles/image/"+proName+"_image"+xl+".jpg"; OutputStream out = new FileOutputStream(filePath); out.write(bytes); out.flush(); out.close(); fckStr = fckStr.replace(link.attr("src"), filePath); xl++; }catch(Exception e){ e.printStackTrace(); } }else{ String tempStr = (requestRealPath+link.attr("src").substring(7)).replaceAll("\\\\", "/"); fckStr=fckStr.replaceFirst(link.attr("src"), tempStr); } } StringReader sr = new StringReader(fckStr); ArrayList (ArrayList for(int k = 0; k < p.size(); ++k) { ph.setFont(font); Element e = p.get(k); List for(Object ch : chs){ Chunk chunk = (Chunk)ch; if(chunk.getImage()!=null){ Image img = chunk.getImage(); img.scalePercent(75f); } } } ph.add(p.get(k)); } return ph; } }