效果图:
如果是maven项目需要添加依赖,普通项目在官网(The Leading PDF Library for Developers | iText)下载对应的jar包加入即可。依赖如下:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
预览下载:
public void review(HttpServletResponse response) throws Exception {
//预览
response.setHeader("content-Type", "application/pdf");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "inline;filename=text.pdf");//页面预览
//response.setHeader("Content-Disposition", "attachment; filename=text.pdf");//直接下载
//新建Document对象并设置纸张大小和边距(左,右, 上,下)
Document document = new Document(PageSize.A4, 10, 20, 30, 40);
//PDF属性(可写可不写)
document.addAuthor("author");//作者
document.addTitle("title");//标题
document.addSubject("subject");//主题
document.addKeywords("keywords");//关键字
try {
//建立书写器(Writer)与document对象关联,以字节流输出
//创建 PdfWriter对象 第一个参数是对文档对象的引用,第二个参数是文件的实际名称,在该名称中还会给出其输出路径。
PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
writer.setPageEvent(new MyHeaderFooter());// 页眉页脚(需要时设置)
//打开document
document.open();
//添加内容
setpdf(document, writer);
//关闭Document对象和书写器(Writer)
if(document != null){
document.close();
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
字节流:
public void getByte() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();//构建字节输出流
//新建Document对象并设置纸张大小和边距(左,右,上,下)
Document document = new Document(PageSize.A4, 10, 20, 30, 40);
//PDF属性(可写可不写)
document.addAuthor("author");//作者
document.addTitle("title");//标题
document.addSubject("subject");//主题
document.addKeywords("keywords");//关键字
try {
//建立书写器(Writer)与document对象关联,以字节流输出
//创建 PdfWriter对象 第一个参数是对文档对象的引用,第二个参数是文件的实际名称,在该名称中还会给出其输出路径。
PdfWriter writer = PdfWriter.getInstance(document, baos);
writer.setPageEvent(new MyHeaderFooter());// 页眉页脚(需要时设置)
//打开document
document.open();
//添加内容
setpdf(document, writer);
//合并pdf
/*PdfReader reader = new PdfReader(new URL("路径"));
PdfContentByte cb = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
cb.addTemplate(page, 0, 0);
}*/
//关闭Document对象和书写器(Writer)
if(document != null){
document.close();
writer.close();
}
} catch (Exception e) {
e.printStackTrace();
}
byte[] bytes = baos.toByteArray();//转byte
return;
}
内容设置:
public Document setpdf(Document document, PdfWriter writer) throws Exception {
Font font1 = new Font(BaseFont.createFont( "/zhttfs/1.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 22);
Font font2 = new Font(BaseFont.createFont( "/zhttfs/1.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 16);
Font font3 = new Font(BaseFont.createFont( "/zhttfs/1.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 16, Font.BOLD);//加粗
Font font4 = new Font(BaseFont.createFont( "/zhttfs/1.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 16, Font.UNDERLINE);//下划线
Font font5 = new Font(BaseFont.createFont( "/zhttfs/7.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 16);//字体:Wingdings 2;用于复选框
Font font6 = new Font(BaseFont.createFont( "/zhttfs/1.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 10);//字号小,用于角标
//font1.setColor(BaseColor.RED);//字体颜色
Paragraph p1 = new Paragraph("标题", font1);
p1.setLeading(39);//行间距
p1.setAlignment(Element.ALIGN_CENTER);//居中
document.add(p1);
//转换html(接下方MyXMLWorkerHelper)
Paragraph context = new Paragraph();
ElementList elementList = MyXMLWorkerHelper.parseToElementList("<p><span style=\"font-size: 36px;\">36px</span><span style=\"font-size: 12px;\">12px</span><span style=\"font-weight: bold;\">加粗</span><span style=\"font-style: italic;\">倾斜</span><span style=\"color: rgb(249, 150, 59);\">颜色</span><span style=\"background-color: rgb(139, 170, 74);\">背景色</span><p style=\"text-align: center;\">居中</p></p>", null);
for (Element element : elementList) {
context.add(element);
}
document.add(context);
//段落
Paragraph p2 = new Paragraph("内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内", font2);
p2.setLeading(32);//行间距
p2.setFirstLineIndent(32);//首行缩进(字号16,缩进两个字就是32)
p2.setIndentationLeft(30);//段落左缩进
p2.setIndentationRight(60);//段落右缩进
document.add(p2);
//加粗
Paragraph p3 = new Paragraph("加粗", font3);
document.add(p3);
//下划线
Paragraph p4 = new Paragraph("下划线", font4);
document.add(p4);
//角标(平方米)
Paragraph p5 = new Paragraph();
p5.add(new Chunk("25m", font2));
Chunk p5chunk = new Chunk("2", font6);
p5chunk.setTextRise(10);//设置偏移
p5.add(p5chunk);
p5.setLeading(32);
document.add(p5);
//同段落加粗下划线角标
Paragraph p99 = new Paragraph();
p99.add(new Chunk("姓名:", font2));
p99.add(new Chunk("张三", font3));
p99.add(new Chunk(" 手机号:", font2));
p99.add(new Chunk(" ", font4));
p99.add(new Chunk(" 面积:", font2));
p99.add(new Chunk("25m", font2));
Chunk p99chunk1 = new Chunk("2", font6);
p99chunk1.setTextRise(10);//设置偏移
p99.add(p99chunk1);
p99.setLeading(32);
document.add(p99);
//选择框
char checked='\uF052'; //选中
char unchecked='\uF0A3';//未选中
Chunk checkon = new Chunk(checked, font5); //字体为Wingdings 2
Chunk checkoff = new Chunk(unchecked, font5);//字体为Wingdings 2
Paragraph p7 = new Paragraph();
p7.add(checkon);
Chunk p7Chunk1 = new Chunk("选中 ");
p7Chunk1.setFont(font2);
p7.add(p7Chunk1);
p7.add(checkoff);
Chunk p7Chunk2 = new Chunk("未选中");
p7Chunk2.setFont(font2);
p7.add(p7Chunk2);
p7.setLeading(32);
document.add(p7);
//画一条红色横线
Paragraph p11 = new Paragraph();
BaseColor lineColor = RED;
LineSeparator lineSeparator = new LineSeparator();
lineSeparator.setLineWidth(3f);
lineSeparator.setLineColor(lineColor);
p11.add(new Chunk(lineSeparator));
document.add(p11);
//合并PDF
PdfReader reader = new PdfReader(new URL("要合并的PDF路径"));
PdfContentByte cb = writer.getDirectContent();
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
document.newPage();
PdfImportedPage page = writer.getImportedPage(reader, i);
cb.addTemplate(page, 0, 0);
}
//表格
//生成一个200列表格(因为表格有可能上下单元格宽度不一致,所以直接定义平均分成200列,每个格子设置合并,但要注意每行单元格Colspan加起来要等于200)
PdfPTable table = new PdfPTable(200);
table.setWidthPercentage(100);//总宽度100%
table.setSpacingBefore(20);//上边距
PdfPCell cell;
cell = new PdfPCell(new Phrase("表头", font2));
cell.setColspan(200);//合并列
cell.setPadding(10);//内边距
cell.setMinimumHeight(36);//最小高度,内容超出自适应
//cell.setFixedHeight(36);//固定高度,内容超出则隐藏
cell.setLeading(0, 1);//设置行高
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
cell.setBorderWidth(1.5f);//边框宽度(0:无边框;1:默认宽度)
cell.setBorderWidthTop(3);//上边框宽度
cell.setBorderWidthBottom(1);//下边框宽度
table.addCell(cell);
cell = new PdfPCell(new Phrase("合并行", font2));
cell.setColspan(50);//合并列
cell.setRowspan(2);//合并行
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("上1", font2));
cell.setColspan(50);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("上2", font2));
cell.setColspan(75);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("上3", font2));
cell.setColspan(25);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("下1", font2));
cell.setColspan(100);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("下2", font2));
cell.setColspan(50);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("对角线", font2));
cell.setColspan(50);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase(" ", font2));
cell.setColspan(150);//合并列
table.addCell(cell);
cell = new PdfPCell(new Phrase("合计(隐藏边框)", font2));
cell.setColspan(200);//合并列
cell.setPadding(10);//内边距
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
cell.disableBorderSide(15);//隐藏全部边框(上:1 下:2 左:4 右:8),隐藏多条边就是几个数相加,例如上左就是1+4=5;隐藏全部就是1+2+4+8=15
table.addCell(cell);
document.add(table);
//画对角线
PdfContentByte canvas = writer.getDirectContent();
Common.drawLine(canvas, 151, 391, 575, 357);
return document;
}
页眉页脚:
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import java.io.IOException;
public class MyHeaderFooter extends PdfPageEventHelper {
//总页数
PdfTemplate totalPage;
//字体
Font hfFont;
{
try {
hfFont = new Font(BaseFont.createFont( "/zhttfs/1.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED), 14);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//打开文档时,创建一个总页数的模版
public void onOpenDocument(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
totalPage = cb.createTemplate(50, 14);//共 页的宽高
}
//一页加载完成触发,写入页眉和页脚
public void onEndPage(PdfWriter writer, Document document) {
//创建一个两列的表格
PdfPTable table = new PdfPTable(2);
try {
table.setTotalWidth(PageSize.A4.getWidth());//总宽度为A4纸张宽度
table.setLockedWidth(true);//锁定列宽
table.setWidths(new int[]{50, 50});//设置每列宽度
PdfPCell cell = new PdfPCell(new Phrase("第"+ document.getPageNumber() +"页/", hfFont));
cell.setHorizontalAlignment(Element.ALIGN_RIGHT);//设置水平右对齐
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
cell.disableBorderSide(15);//隐藏全部边框
table.addCell(cell);
PdfPCell cell1 = new PdfPCell(Image.getInstance(totalPage));//共 页
cell1.setHorizontalAlignment(Element.ALIGN_LEFT);//设置水平左对齐
cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
cell1.disableBorderSide(15);//隐藏全部边框
table.addCell(cell1);
table.writeSelectedRows(0, -1, 0, 50, writer.getDirectContent());
} catch (Exception e) {
throw new ExceptionConverter(e);
}
//生成左侧页眉
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_LEFT, new Paragraph("左页眉", hfFont),
document.left(), PageSize.A4.getHeight() -30, 0);
//生成右侧页眉
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_RIGHT, new Paragraph("右页眉", hfFont),
document.right(), PageSize.A4.getHeight() - 30, 0);
}
// 全部完成后,将总页数的pdf模版写到指定位置
public void onCloseDocument(PdfWriter writer,Document document) {
String text = "共" + (writer.getPageNumber()) + "页";
ColumnText.showTextAligned(totalPage, Element.ALIGN_MIDDLE, new Paragraph(text, hfFont), 0, 0, 0);
}
}
html识别
import com.itextpdf.text.Font;
import com.itextpdf.tool.xml.ElementList;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFile;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliers;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.ElementHandlerPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class MyXMLWorkerHelper {
public static class MyFontsProvider extends XMLWorkerFontProvider {
public MyFontsProvider() {
super(null, null);
}
@Override
public Font getFont(final String fontname, String encoding, float size, final int style) {
String fntname = fontname;
if (fntname == null) {
fntname = "宋体";
}
return super.getFont(fntname, encoding, size, style);
}
}
public static ElementList parseToElementList(String html, String css) throws IOException {
// CSS
CSSResolver cssResolver = new StyleAttrCSSResolver();
if (css != null) {
CssFile cssFile = XMLWorkerHelper.getCSS(new ByteArrayInputStream(css.getBytes()));
cssResolver.addCss(cssFile);
}
// HTML
MyFontsProvider fontProvider = new MyFontsProvider();
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
htmlContext.autoBookmark(false);
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline end = new ElementHandlerPipeline(elements, null);
HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, end);
CssResolverPipeline cssPipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
// XML Worker
XMLWorker worker = new XMLWorker(cssPipeline, true);
XMLParser p = new XMLParser(worker);
html = html.replaceAll("<div", "<span")
.replaceAll("/div>", "/span><br/>")
.replaceAll("<br[^>]*>", "<br/>")
.replace("<hr>", "")
.replace("<img>", "")
.replace("<param>", "")
.replace("<link>", "")
.replaceAll("<strong>", "")
.replaceAll("</strong>", "")
.replaceAll("<[/]?(font|FONT|xml|XML|del|DEL|ins|INS|meta|META|spanyes|[ovwxpOVWXP]:\\w+)[^>]*?>", "")
.replaceAll("<([^>]*)(?:lang|LANG|size|SIZE|face|FACE|spanyes|[ovwxpOVWXP]:\\w+)=(?:'[^']*'|\"\"[^\"\"]*\"\"|[^>]+)([^>]*)>", "")
.replaceAll("<!--.*?-->", "");
p.parse(new ByteArrayInputStream(html.getBytes()));
return elements;
}
}
行首标点长数字字母断行处理
注:随便放到项目中能引用的地方就可以,使用方式在下方
import com.itextpdf.text.SplitCharacter;
import com.itextpdf.text.pdf.DefaultSplitCharacter;
import com.itextpdf.text.pdf.PdfChunk;
public class ChineseSplitCharater implements SplitCharacter {
//不允许开头的字符
static final String[] NOT_BEGIN_CHARACTERS = {"・", "•", "・", "」", ")", "!", "%", ")", ",", ".", "?", "]", "}", "。", "ー", "゙", "゚", "、", "。", ",", ".", ":", ";", "?", "!", "゛", "゜", "ヽ", "’", "”", "〕", "]", "}", "〉", "》", "』", "】", "°", "′", "″", " "};
//不允许结尾的字符
static final String[] NOT_ENDING_CHARACTERS = {"$", "(", "[", "{", "£", "¥", "“", "‘", "《", "〈", "「", "『", "【", "〔", "「", "(", "[", "{", "¥", "$", " "};
//不允许上一行未满行就换行的字符
static final String[] NOT_BREAK_CHARACTERS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
/**
* An instance of the jpSplitCharacter.
*/
public static final ChineseSplitCharater SplitCharacter = new ChineseSplitCharater();
/**
* An instance DefaultSplitCharacter used for BasicLatin characters.
*/
private static final SplitCharacter defaultSplitCharacter = new DefaultSplitCharacter();
public ChineseSplitCharater() {}
/**
* Custom method to for SplitCharacter to handle Japanese characters.
* Returns <CODE>true</CODE> if the character can split a line. The splitting implementation
* is free to look ahead or look behind characters to make a decision.
*
* @param start the lower limit of <CODE>cc</CODE> inclusive
* @param current the pointer to the character in <CODE>cc</CODE>
* @param end the upper limit of <CODE>cc</CODE> exclusive
* @param cc an array of characters at least <CODE>end</CODE> sized
* @param ck an array of <CODE>PdfChunk</CODE>. The main use is to be able to call
* {@link PdfChunk#getUnicodeEquivalent(int)}. It may be <CODE>null</CODE>
* or shorter than <CODE>end</CODE>. If <CODE>null</CODE> no conversion takes place.
* If shorter than <CODE>end</CODE> the last element is used
* @return <CODE>true</CODE> if the character(s) can split a line
*/
public boolean isSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) {
// Note: If you don't add an try/catch and there is an issue with isSplitCharacter(), iText silently fails and
// you have no idea there was a problem.
try {
char charCurrent = getCharacter(current, cc, ck);
int next = current + 1;
if (next < cc.length) {
char charNext = getCharacter(next, cc, ck);
for (String not_begin_character : NOT_BEGIN_CHARACTERS) {
if (not_begin_character.equals(String.valueOf(charNext))) {
return false;
}
}
}
for (String not_ending_character : NOT_ENDING_CHARACTERS) {
if (not_ending_character.equals(String.valueOf(charCurrent))) {
return false;
}
}
for (String not_break_character : NOT_BREAK_CHARACTERS) {
if (not_break_character.equals(String.valueOf(charCurrent))) {
return true;
}
}
boolean isBasicLatin = Character.UnicodeBlock.of(charCurrent) == Character.UnicodeBlock.BASIC_LATIN;
if (isBasicLatin)
return defaultSplitCharacter.isSplitCharacter(start, current, end, cc, ck);
return true;
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
/**
* Returns a character int the array (Note: modified from the iText default version with the addition null
* check of '|| ck[Math.min(position, ck.length - 1)] == null'.
*
* @param position position in the array
* @param ck chunk array
* @param cc the character array that has to be checked
* @return the character
*/
protected char getCharacter(int position, char[] cc, PdfChunk[] ck) {
if (ck == null || ck[Math.min(position, ck.length - 1)] == null) {
return cc[position];
}
return (char) ck[Math.min(position, ck.length - 1)].getUnicodeEquivalent(cc[position]);
}
}
使用方式:
Chunk chunk = new Chunk("内容内容内容内容内容内容,内容,内容,内容内容内容123123123123123123123123123内容内容内容内容内容内容内容内容内容内容内abcdefghijklmnopqrstuvwxyz内容内容内容内容内容内容内容ABCDEFGHIJKLMNOPQOSTUVWXYZ", font2);
chunk.setSplitCharacter(ChineseSplitCharater.SplitCharacter);//调用设置格式,需用Chunk
Paragraph p2 = new Paragraph(chunk);
document.add(p2);
效果:
未使用
使用后:
持续更新。。。