<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- itextpdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<!-- itext水印-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.47</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.47</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>core-renderer</artifactId>
<version>R8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<!-- 渲染 css 样式 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
</dependencies>
需要5个步骤
创建文档
Document document = new Document();
生成PDF
PdfWriter.getInstance(doc, new FileOutputStream(DEST+ File.separator+"HelloWorld.pdf") );
打开PDF
document.open();
往PDF中写入内容
document.add(new Paragraph("Hello World"));
关闭PDF
document.close();
完整版
Document doc = new Document();
PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR +"createSamplePDF.pdf"));
doc.open();
doc.add(new Paragraph("Hello World"));
doc.close();
在操作演示PDF前我们需要知道如何获取 classes目录的路径 (项目打包后的根路径)
只有这样我们生成的PDF会在项目内部(当然如果你不需要PDF在项目的classes目录而且在其他地方,自行补充路径)
private static String FILE_DIR = null;
static {
try {
FILE_DIR = Paths.get(ResourceUtils.getURL("classpath:").getPath().substring(1)).toString()+ File.separator;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
我们还需要知道一个问题就是itext默认不支持中文(默认隐藏中文字体只显示英文)
我们可以在添加中文时候的那个对象中(基本都有设置字体格式的方法) 添加下面方法,就能识别中文了
对象.setFont(PdfUtils.getChineseFont());
在块.短语,段落,的构造第二个参数是可以直接添加PdfUtils.getChineseFont()
中文字体工具类
package com.pdf.utils;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.BaseFont;
import java.io.IOException;
public class PdfFont {
// color= BaseColor.BLUE(蓝色) BaseColor.BLACK(黑色) new BaseColor(105, 105, 105)(文本色)
// style= Font.ITALIC细,Font.NORMA正常L,Font.BOLD粗
// size=字体大小
public static Font getChineseFont( int size,int style,BaseColor color) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese, size, style, color);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
public static Font getChineseFont(int size, int style) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese, size, style, new BaseColor(105, 105, 105));
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
public static Font getChineseFont( int size) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese, size, Font.NORMAL, new BaseColor(105, 105, 105));
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
public static Font getChineseFont(int style,BaseColor color) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese,style, Font.NORMAL, color);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
public static Font getChineseFont( long size, BaseColor color) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese, size, Font.NORMAL, color);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
public static Font getChineseFont( BaseColor color) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese, 15, Font.NORMAL, color);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
// 默认配置 黑色字体 大小15 不加粗
public static Font getChineseFont( ) {
BaseFont bfChinese;
Font fontChinese = null;
try {
bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
fontChinese = new Font(bfChinese, 15, Font.NORMAL, new BaseColor(105, 105, 105));
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fontChinese;
}
}
需要倒入的包
import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.List;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.draw.DottedLineSeparator;
import com.itextpdf.text.pdf.draw.LineSeparator;
import com.itextpdf.text.pdf.draw.VerticalPositionMark;
import lombok.SneakyThrows;
import org.junit.Test;
import org.springframework.util.ResourceUtils;
import java.awt.*;
import java.io.*;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
注意:看每个配置 哪行代码的后面 如果顺序错了那么久无效了
配置PDF
页面大小
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
不止B4你进入PageSize类里有一大堆 常用B4
比如:
页面背景
不设置默认为白色
Rectangle rect = new Rectangle(PageSize.B4.rotate());
rect.setBackgroundColor(new BaseColor(199 , 237, 204) ); //设置颜色
Document doc = new Document(rect);
推荐几款护眼的颜色:
new BaseColor(199 , 237, 204) 豆沙绿(推荐)
new BaseColor(200, 200, 169) 淡青色
new BaseColor(254, 67, 101) 淡红色
new BaseColor(252, 167, 154) 淡粉色
new BaseColor(249, 205, 173) 淡黄色
1.
PDF版本设置(默认1.4)
PdfWriter writer = PdfWriter.getInstance(xxxxxx);
writer.setPdfVersion(PdfWriter.PDF_VERSION_1_2); //设置版本号 一般不设置默认就可以
PDF文档属性(Title,Author,Subject,Keywords )
Document doc = new Document();
PdfWriter writer = PdfWriter.getInstance(xxxxxx);
//文档属性 注意: 如果设置了版本号那么需要在版本号后面写
doc.addTitle("标题");
doc.addAuthor("作者");
doc.addSubject("主题");
doc.addKeywords("关键字");
doc.addCreator("创建者");
文档属性可以有可无 看你心情
页边空白
Document doc = new Document();
PdfWriter writer = PdfWriter.getInstance(xxxxxx);//必须在这行后面
doc.setMargins(10, 20, 30, 40); //页边空白 左 右 上 下
配置后的效果
PDF密码
就相当于给文件加密需要使用密码验证后才能观看
Document doc = new Document();
PdfWriter writer = PdfWriter.getInstance(xxxxxx);//必须在这行后面
// 设置密码
//第一个参数是用户密码(允许复制,签名 不允许打印,编辑)
//第二个参数是管理员密码(允许打印,编辑,复制,签名 加密级别)
writer.setEncryption("user".getBytes(), "admin".getBytes(),
PdfWriter.ALLOW_COPY, // 允许复制,签名 不允许打印,编辑
PdfWriter.STANDARD_ENCRYPTION_128); //允许打印,编辑,复制,签名 加密级别
添加PDF页 看代码你就懂了
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "createSamplePDF.pdf"));
doc.open(); //打开PDF 默认为第一页
doc.add(new Paragraph("First page")); // 给第一个添加内容
doc.add(new Paragraph("Hello World"));
doc.newPage(); // 创建第2页
writer.setPageEmpty(false); //可以允许第2页为空 如果不设置那么PDF默认给没有内容的页 取消掉
doc.newPage(); // 创建第3页
doc.add(new Paragraph("New page"));
doc.close();
//删除PDF页 // 看代码你就懂了
只选择指定类其他的删除
PdfReader reader = new PdfReader(FILE_DIR + "createSamplePDF.pdf");
// 从原PDF中抽取指定页 生成
reader.selectPages("1,3,5"); //只选择1和3页和第5页
//生成新的PDF
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(FILE_DIR
+ "createSamplePDF_de.pdf"));
stamp.close();
reader.close();
只选择指定范围页其他页删除
/** * 截取pdfFile的第from页至第end页,组成一个新的文件名 * @param respdfFile 需要分割的PDF * @param savepath 新PDF * @param from 起始页 * @param end 结束页 */
public static void splitPDFFile(String respdfFile, String savepath, int from, int end) {
Document document = null;
PdfCopy copy = null;
try {
PdfReader reader = new PdfReader(respdfFile);
int n = reader.getNumberOfPages();
if(end==0){
end = n;
}
ArrayList<String> savepaths = new ArrayList<String>();
savepaths.add(savepath);
document = new Document(reader.getPageSize(1));
copy = new PdfCopy(document, new FileOutputStream(savepaths.get(0)));
document.open();
for(int j=from; j<=end; j++) {
document.newPage();
PdfImportedPage page = copy.getImportedPage(reader, j);
copy.addPage(page);
}
document.close();
} catch (IOException e) {
e.printStackTrace();
} catch(DocumentException e) {
e.printStackTrace();
}
}
如果你对BaseFont类自带的字体颜色不满足的话我们可以自己进行配色
这个网站是rgb颜色表 http://www.wahart.com.hk/rgb.htm new BaseColor(r, g, b);
Chunk : 块,PDF文档中描述的最小原子元素 ,其他高级的文本对象都是基于Chunk的
在水平方向,Chunk的字符满一行,就会从头开始。请注意,这是从头开始,而不是另起一行。对于Chunk来说,行间距默认为0,那么当文档中只有Chunk时,这些字符永远只会出现再第一行。
注意:块一般不会单独使用而且和其他文本对象进行组合使用的
doc.open(); //打开PDF 默认为第一页
//定义一个块
Chunk chunk = new Chunk("Cat");
//设置块的背景色 (可选)
// chunk.setBackground(BaseColor.WHITE); //白色背景(默认)
//设置快内的字体为可识别中文字体
//字体样式,编码格式,插入方式,字体大小,字体样式(Font.ITALIC细,Font.NORMA正常L,Font.BOLD粗),字体颜色)
Font font = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED,17, Font.NORMAL, new BaseColor(105, 105, 105));
//设置快内的字体颜色
chunk.setFont(font);
//增加块到文档
doc.add(chunk);
doc.close();
如果单独使用块的话-块重叠案例
doc.open(); //打开PDF 默认为第一页
//建块
int i=0;
for(i=1; i<=11; i++){
doc.add(new Chunk("This is sentence "+i+". "));
}
doc.close();
可以发现重叠了 ,所以块一般是不能单独使用的,我们要配合下面的更高级文本对象进行使用,进行改变一条句子中的某些字的样式
Phrase作用是添加一个短句。短语类知道如何添加行与行之间的间距。 但是没法和Chunk一样设置样式
doc.open(); //打开PDF 默认为第一页
//添加短句
int i=0;
for(i=1; i<11; i++){
doc.add(new Phrase("This is sentence "+i+". "));
}
doc.close();
可以看到了和Chunk不同的是,会自动换行了,既然Chunk可以 设置样式Phrase可以自动换行那么我们将他们组合不就可以了
Phrase组合Chunk使用
doc.open(); //打开PDF 默认为第一页
//定义一个块
Chunk chunk1 = new Chunk("哈哈哈哈哈哈1");
//添加横线 比如下划线(0.2f, -2f) 删除线(0.2f, 4f) 第一个参数是浓度 ,第二个参数是位置 横线位置
chunk1.setUnderline(0.2f, 4f);
Font font1 = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED,17, Font.NORMAL, new BaseColor(105, 105, 105));
chunk1.setFont(font1);
Chunk chunk2 = new Chunk("惺惺惜惺惺想寻2");
chunk2.setBackground(new BaseColor(200, 200, 169) );//添加块背景颜色
Font font2 = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED,22, Font.NORMAL, new BaseColor(105, 105, 105));
chunk2.setFont(font2);
//将块添加到短句里
Phrase elements = new Phrase(50); //设置行间距
elements.add(chunk1);
elements.add(chunk2);
doc.add(elements);
doc.close();
这样Phrase就能有Chunk的样式效果了
doc.open(); //打开PDF 默认为第一页
//定义段落
Paragraph paragraph = new Paragraph();
//插入十条文本块到段落中
int i=0;
for(i=0; i<10; i++){
Chunk chunk = new Chunk("This is a sentence which is long " + i + ". ");
paragraph.add(chunk);
}
//添加段落
doc.add(paragraph);
doc.close();
Paragraph 是段落的处理。在一个段落中 每一个段落都会结尾自带换行,
你可以设置段落的对齐方式,缩进和间距。 简单来说就是对Phrase进行升级了
可以操控文本在PDF中的位置了
doc.open(); //打开PDF 默认为第一页
//定义段落
Paragraph paragraph = new Paragraph();
//插入10条文本块到段落中
int i=0;
for(i=0; i<10; i++){
Chunk chunk = new Chunk("This is a sentence which is long " + i + ". ");
paragraph.add(chunk);
}
doc.add(paragraph);
doc.close();
暂时看来和短句的运行效果差不多,每句都在自己的行。
您可以设置一个段落前后的间距。 也就是行的上间距和下间距
paragraph.setSpacingAfter(50);
paragraph.setSpacingBefore(50);
您可以设置使用setAlignment()方法的段落的对齐方式。
paragraph.setAlignment(Element.ALIGN_LEFT); //靠左
paragraph.setAlignment(Element.ALIGN_CENTER); //居中
paragraph.setAlignment(Element.ALIGN_RIGHT); //靠右
您可以设置该段左,右缩进。
paragraph.setIndentationLeft(50);
paragraph.setIndentationRight(50);
案例演示
doc.open(); //打开PDF 默认为第一页
//定义段落1
Paragraph paragraph = new Paragraph("This is a sentence which is long . ");
//设置段段落居中
paragraph.setAlignment(Element.ALIGN_CENTER);
//设置段落行缩进
paragraph.setIndentationLeft(10); //左边缩进
//添加段落
doc.add(paragraph);
//定义段落2
Paragraph paragraph1 = new Paragraph("This is a sentence which is long . ");
//设置段落行的上间距
paragraph1.setSpacingAfter(20);
//设置段段落居中
paragraph1.setAlignment(Element.ALIGN_CENTER);
//设置段落行缩进
paragraph1.setIndentationLeft(150); //左边缩进
//添加段落
doc.add(paragraph1);
doc.close();
拿什么时候使用Chunk 什么时候使用Phrase 什么时候使用Paragraph呢?
Chunk: 只能设置字体和背景样式 ,而且内容只能在一行中
Phrase: 行满自动换行,可以设置行高, 但是不能手动换行,不能设置字体样式
Paragraph: 除了不能设置字体样式和背景外 基本文本所能用于的特性都可以实现 ,每个Paragraph结尾自带换行
通俗易懂的话来讲就是 Paragraph用来设置内容排版 Phrase用来拼接语句的, Chunk用来设置字体和背景样式的
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "createSamplePDF.pdf"));
doc.open(); //打开PDF 默认为第一页
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.3f);//透明度
PdfContentByte canvas = instance.getDirectContent();
canvas.setGState(gs);
Phrase phrase1 = new Phrase("This is a test!left");
Phrase phrase2 = new Phrase("This is a test!right");
Phrase phrase3 = new Phrase("This is a test!center");
// 参数1 默认就行 ,参数2默认就行,参数3内容,参数4x轴,参数5y轴,参数6字体旋转
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase1, 100, 500, 10);
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase2, 150, 536, 40);
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase3, 200, 572, 60);
doc.close();
注意 :起始位置是左上角 x 是正数=往右 y是正数=往上 否则反之
有序列表 无需列表 字母列表
注意导包 import com.itextpdf.text./*; 而不是java.util
有序列表
doc.open(); //打开PDF 默认为第一页
List orderedList = new List(List.ORDERED);
for (int i = 0; i < 10; i++) {
orderedList.add(new ListItem("Item "));
}
doc.add(orderedList);
doc.close();
无序列表
doc.open(); //打开PDF 默认为第一页
List unorderedList = new List(List.UNORDERED);
for (int i = 0; i < 10; i++) {
unorderedList.add(new ListItem("Item "));
}
doc.add(unorderedList);
doc.close();
手指列表
doc.open(); //打开PDF 默认为第一页
ZapfDingbatsList zapfDingbatsList = new ZapfDingbatsList(43, 30);
for (int i = 0; i < 10; i++) {
zapfDingbatsList.add(new ListItem("Item "));
}
doc.add(zapfDingbatsList);
列表嵌套
doc.open(); //打开PDF 默认为第一页
// 父列表
List orderedList = new List(List.ORDERED);
orderedList.add(new ListItem("Item "));
// 子列表
List unorderedList = new List(false, false, 30);
for (int i = 0; i < 5; i++) {
//取消列表符合
unorderedList.setListSymbol(new Chunk("", FontFactory.getFont(FontFactory.HELVETICA, 6)));
unorderedList.add(new ListItem("Item"));
}
orderedList.add(unorderedList);
doc.add(orderedList);
doc.close();
Anchor,相当于html中的超链接,主要实现2个功能:
1.跳转到外部站点。
2.跳转到文档的特定位置。
跳转到特定网站,只需要设置Anchor对象的Reference属性
调转到文档的特定位置(在HTML中是描点),先要创建一个Anchor对象到特定位置,并为其命名,如“China”,然后,在创建一个Anchor至于需要点击的位置,设置其Reference属性为“/#China”
演示一个锚链接
doc.open(); //打开PDF 默认为第一页
// Anchor超链接和锚点对象: internal and external links
Anchor dest = new Anchor("连接到设置的UN锚点", PdfFont.getChineseFont(BaseColor.BLUE));
dest.setName("CN"); // 设置锚点
dest.setReference("#UN");// 跳转到UN锚点
doc.add(dest);
doc.newPage(); // 创建第2页
instance.setPageEmpty(false); //可以允许第2页为空 如果不设置那么PDF默认给没有内容的页 取消掉
doc.newPage(); // 创建第3页
Anchor toUS = new Anchor("连接到设置的CN锚点。", PdfFont.getChineseFont(BaseColor.BLUE));
toUS.setName("UN");
toUS.setReference("#CN");// 跳转到CN锚点
doc.add(toUS);
doc.close();
锚链可以制作目录和导航
演示创建超链接跳转百度
doc.open(); //打开PDF 默认为第一页
Anchor dest = new Anchor("超链接", PdfFont.getChineseFont(BaseColor.BLUE));
dest.setReference("http://www.baidu.com");// 如果不是描点,那么我们可以使用超链接连接
doc.add(dest);
doc.close();
doc.open(); //打开PDF 默认为第一页
//定义段落
Paragraph paragraph = new Paragraph();
//添加段落内容
paragraph.add(new Phrase("This is a chapter."));
//定义章
Chapter chapter = new Chapter(paragraph, 1);
//添加章-节内容
chapter.addSection("This is section 1", 2);
chapter.addSection("This is section 2", 2);
//添加章节
doc.add(chapter);
doc.close();
代码中chapter.addSection(“This is section 1”, 2)中的2是设置深度,如果设置成1跟章得头是一个级别了。
如果同一个图像创建多次并都添加到文档中,文档的大小就增长很快,如果一个图像要在多个地方使用,只要生成一个Image对象,在添加到文档前,设置其属性就可以了,没有必要创建多份。
创建图像
Image img = Image.getInstance(FILE_DIR+"baidu.jpg");
doc.add(img);
图片的配置
img.setBorder(Image.BOX) //边框,
img.setBorderWidth(10)//边框宽度
img.setBorderColor(BaseColor.WHITE) //边框颜色,
img.setRotationDegrees(-30) //旋转 正向左旋转 负向右旋转
img.setAbsolutePosition(x,y) //绝对位置 0,0 是最左下角 x变大向右移动 y变大向上移动
缩放图片
img.scaleAbsolute(150, 150);//绝对宽和高
img.scaleAbsoluteWidth(150);//绝对宽
img.scaleAbsoluteHeight(150);//绝对高
img.scalePercent(50); //百分比
img.scaleToFit(400,300);//像素大小
对其方式
img.setAlignment(Image.LEFT)
// Image.LEFT(最左边) Image.RIGHT(最右边) Image.TOP(中)
图片自适应大小
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
Image img = Image.getInstance(FILE_DIR+"37.jpg");
img.scaleToFit(rect.getWidth(), rect.getHeight()); //关键代码
doc.add(img);
doc.close();
简单小演示
doc.open(); //打开PDF 默认为第一页
Paragraph paragraph = new Paragraph("你妹的", PdfFont.getChineseFont(BaseColor.BLACK));
paragraph.setAlignment(Element.ALIGN_CENTER);
doc.add(paragraph );
//插入图片
Image img = Image.getInstance(FILE_DIR+"baidu.jpg");
img.setAlignment( Image.RIGHT); //图片靠右
img.scaleToFit(200, 100);//大小
img.setRotationDegrees(-30);//旋转
doc.add(img);
//定义段落
Paragraph paragraph1 = new Paragraph("作者:xxxxxxxxx",PdfFont.getChineseFont(BaseColor.BLACK));
paragraph1.setAlignment(Element.ALIGN_RIGHT);
doc.add(paragraph1);
doc.close();
箭头
doc.open(); //打开PDF 默认为第一页
doc.add(new VerticalPositionMark() {
public void draw(PdfContentByte canvas, float llx, float lly,
float urx, float ury, float y) {
canvas.beginText();
BaseFont bf = null;
try {
bf = BaseFont.createFont(BaseFont.ZAPFDINGBATS, "", BaseFont.EMBEDDED);
} catch (Exception e) {
e.printStackTrace();
}
canvas.setFontAndSize(bf, 12);
// LEFT
canvas.showTextAligned(Element.ALIGN_CENTER, String.valueOf((char) 220), llx - 10, y, 0);
// RIGHT
canvas.showTextAligned(Element.ALIGN_CENTER, String.valueOf((char) 220), urx + 10, y + 8, 180);
canvas.endText();
}
});
doc.close();
直线
doc.open(); //打开PDF 默认为第一页
Paragraph p1 = new Paragraph("LEFT");
p1.add(new Chunk(new LineSeparator()));
p1.add("RIGHT");
doc.add(p1);
doc.close();
点线
Paragraph p1 = new Paragraph("LEFT");
p1.add(new Chunk(new DottedLineSeparator()));
p1.add("RIGHT");
doc.add(p1);
下划线
doc.open(); //打开PDF 默认为第一页
LineSeparator UNDERLINE = new LineSeparator(1, 100, null, Element.ALIGN_CENTER, -2);
Paragraph p3 = new Paragraph();
p3.setFont(PdfFont.getChineseFont());//设置中文支撑
p3.add("三生三世惺惺惜惺惺想寻寻寻寻寻寻寻寻寻寻寻寻寻");
p3.add(UNDERLINE);//下划线
p3.add("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN");
p3.add(UNDERLINE);//下划线
doc.add(p3);
doc.close();
https://www.cnblogs.com/liaojie970/p/7132475.html 设置段落位置
分为图片水印和字体水印 因为有点复杂的缘故我直接封装好了工具类直接可以使用 ,但是也不是一成不变的,
需要你根据情况进行调整大小,一般默认配置就够用了
注意:设置水印和设置图片不是一个概念,如果PDF有背景颜色的话会将水印盖住这个需要注意 ,
因为水印是PDF文件生成好后,后期添加上去的,在堆层的最底层
PdfWatermark水印工具类
package com.pdf.utils;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.*;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class PdfWatermark {
/** * 文字水印 * <p> * 中间或者两边水印 * * @param bos 添加完水印的输出 * @param input 原PDF文件输入 * @param word 水印内容 * @param model 水印添加位置1中间,2两边 */
public static void setWatermark(String bos, String input, String word, int model)
throws DocumentException, IOException {
PdfReader reader = new PdfReader(new FileInputStream(input));
PdfStamper stamper = new PdfStamper(reader,new BufferedOutputStream(new FileOutputStream(bos) ));
PdfContentByte content;
// 创建字体,第一个参数是字体路径,itext有一些默认的字体比如说:
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.3f);//水印透明度
// 获取PDF页数
int total = reader.getNumberOfPages();
// 遍历每一页
for (int i = 0; i < total; i++) {
float width = reader.getPageSize(i + 1).getWidth(); // 页宽度
float height = reader.getPageSize(i + 1).getHeight(); // 页高度
content = stamper.getOverContent(i + 1);// 内容
content.beginText();//开始写入文本
content.setGState(gs);
content.setColorFill(BaseColor.LIGHT_GRAY);
content.setTextMatrix(70, 200);//设置字体的输出位置
if (model == 1) { //平行居中的3条水印
content.setFontAndSize(base, 40); //字体大小
//showTextAligned 方法的参数分别是(文字对齐方式,位置内容,输出水印X轴位置,Y轴位置,旋转角度)
content.showTextAligned(Element.ALIGN_CENTER, word, width / 2, height/3, 30);
content.showTextAligned(Element.ALIGN_CENTER, word, width / 2, height/2, 30);
content.showTextAligned(Element.ALIGN_CENTER, word, width / 2, height/4-50, 30);
} else { // 左右两边个从上到下4条水印
float rotation = 30;// 水印旋转度数
content.setFontAndSize(base, 20);
content.showTextAligned(Element.ALIGN_LEFT, word, 20, height - 50, rotation);
content.showTextAligned(Element.ALIGN_LEFT, word, 20, height / 4 * 3 - 50, rotation);
content.showTextAligned(Element.ALIGN_LEFT, word, 20, height / 2 - 50, rotation);
content.showTextAligned(Element.ALIGN_LEFT, word, 20, height / 4 - 50, rotation);
content.setFontAndSize(base, 22);
content.showTextAligned(Element.ALIGN_RIGHT, word, width - 20, height - 50, rotation);
content.showTextAligned(Element.ALIGN_RIGHT, word, width - 20, height / 4 * 3 - 50, rotation);
content.showTextAligned(Element.ALIGN_RIGHT, word, width - 20, height / 2 - 50, rotation);
content.showTextAligned(Element.ALIGN_RIGHT, word, width - 20, height / 4 - 50, rotation);
}
content.endText();//结束写入文本
}
stamper.close();
reader.close();
}
/** * 给pdf文件添加水印 (注意如果有添加背景颜色的话那么 图片水印将失效被盖住了) * * @param outPdfFile 加了水印后要输出的路径 * @param InPdfFile 要加水印的原pdf文件路径 * @param markImagePath 水印图片路径 * @throws Exception */
public static void addPdfMark( String outPdfFile,String InPdfFile, String markImagePath) throws Exception {
PdfReader reader = new PdfReader(InPdfFile, "PDF".getBytes());
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(outPdfFile));
PdfGState gs = new PdfGState();
Image img = Image.getInstance(markImagePath);// 插入水印
img.setRotationDegrees(30);//旋转 角度
// img.scaleAbsolute(200,100);//自定义图片大小
img.scalePercent(50);//图片依照比例缩放 百分之50
gs.setFillOpacity(0.3f);//水印透明度
int total = reader.getNumberOfPages(); //全部页
PdfContentByte content;
for (int i = 0; i < total; i++) {
content = stamp.getUnderContent(+ 1); // 内容
content.setGState(gs);
float width = reader.getPageSize(i + 1).getWidth(); // 页宽度
float height = reader.getPageSize(i + 1).getHeight(); // 页高度
img.setAbsolutePosition(width / 2, height/3); //图片位置 第1张图
content.addImage(img);
img.setAbsolutePosition(width / 2, height/2); //图片位置 第2张图
content.addImage(img);
img.setAbsolutePosition(width / 2, height/4-50); //图片位置 第3张图
content.addImage(img);
}
stamp.close();// 关闭
reader.close();
}
}
使用教程
文字水印 (原PDF可以有背景颜色) 使用的最多
参数1 是加完水印后的PDF存储位置 参数2是原PDF 参数3是 添加的文字水印 参数4是位置 (1中间,2两边)
@Test
public void test2() throws Exception { //文字水印
PdfWatermark.setWatermark(FILE_DIR + "createSamplePDF_sui.pdf", FILE_DIR + "createSamplePDF.pdf", "测试打印", 1);
}
参数4:是1的情况
参数4:是2的情况
图片水印 (原PDF不能有背景颜色 否则图片水印无效)
参数1 是加完水印后的PDF存储位置 参数2是原PDF 参数3是水印图片位置
@Test
public void test2_1() throws Exception { //图片水印
PdfWatermark.addPdfMark(FILE_DIR + "createSamplePDF_sui.pdf", FILE_DIR + "createSamplePDF.pdf", FILE_DIR + "baidu.jpg");
}
/** * * * @param pdfSourceFile 原文件 * @param pdfDestinationFile 处理后新生成的文件 */
public static void removeBlankPdfPages(String pdfSourceFile, String pdfDestinationFile)
{
try
{
// step 1: create new reader
PdfReader r = new PdfReader(pdfSourceFile);
RandomAccessFileOrArray raf = new RandomAccessFileOrArray(pdfSourceFile);
Document document = new Document(r.getPageSizeWithRotation(1));
// step 2: create a writer that listens to the document
PdfCopy writer = new PdfCopy(document, new FileOutputStream(pdfDestinationFile));
// step 3: we open the document
document.open();
// step 4: we add content
PdfImportedPage page = null;
//loop through each page and if the bs is larger than 20 than we know it is not blank.
//if it is less than 20 than we don't include that blank page.
for (int i=1;i<=r.getNumberOfPages();i++)
{
//get the page content
byte bContent [] = r.getPageContent(i,raf);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
//write the content to an output stream
bs.write(bContent);
//add the page to the new pdf
if (bs.size() > 20)
{
page = writer.getImportedPage(r, i);
writer.addPage(page);
}
bs.close();
}
//close everything
document.close();
writer.close();
raf.close();
r.close();
}
catch(Exception e)
{
//do what you need here
}
}
这种压缩是在生成PDF的时候直接写入到zip里
//创建一个zip
ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(FILE_DIR + "zipPDF.zip"));
//创建PDF文件
ZipEntry entry = new ZipEntry("hello_" + 1 + ".pdf");
//将PDF添加到ZIP
zip.putNextEntry(entry);
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, zip);
// 后面内容
writer.setCloseStream(false);
document.open();
document.add(new Paragraph("Hello " + 1));
document.close();
zip.closeEntry();
zip.close();
读取所有文档然后追加到新的文档
/** * * @param fileList 文档地址 * @param savepath 合并后地址 */
public static void mergepdf(ArrayList<String> fileList, String savepath) {
Document document = null;
try {
document = new Document(new PdfReader(fileList.get(0)).getPageSize(1));
PdfCopy copy = new PdfCopy(document, new FileOutputStream(savepath));
document.open();
for (int i = 0; i < fileList.size(); i++) {
PdfReader reader = new PdfReader(fileList.get(i));
copy.addDocument(reader);
reader.close();
}
} catch (IOException | DocumentException e) {
e.printStackTrace();
} finally {
if (document != null) {
document.close();
}
}
}
@Test
public void he() throws IOException, DocumentException {
ArrayList<String> arrayList=new ArrayList();
arrayList.add(FILE_DIR + "splitPDF1.pdf");
arrayList.add(FILE_DIR + "splitPDF2.pdf");
mergepdf(arrayList, FILE_DIR + "mergePDF.pdf");
}
读取PDF文档的页,然后一页一页追加到新的文档。 (也就是可以控制合并的页)
/** * * @param fileList 文档地址 * @param savepath 合并后地址 */
public static void mergepdf(ArrayList<String> fileList, String savepath) {
Document document = null;
try {
document = new Document(new PdfReader(fileList.get(0)).getPageSize(1));
PdfCopy copy = new PdfCopy(document, new FileOutputStream(savepath));
document.open();
for (int i = 0; i < fileList.size(); i++) {
PdfReader reader = new PdfReader(fileList.get(i));
copy.addDocument(reader);
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
if (document != null) {
document.close();
}
}
}
@Test
public void he() throws IOException, DocumentException {
ArrayList<String> arrayList=new ArrayList();
arrayList.add(FILE_DIR + "splitPDF1.pdf");
arrayList.add(FILE_DIR + "splitPDF2.pdf");
mergepdf(arrayList, FILE_DIR + "mergePDF.pdf");
}
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
instance.setLinearPageMode();
doc.open(); //打开PDF 默认为第一页
doc.add(new Paragraph("1 page"));
//给第一页添加批注
doc.add(new Annotation("Title", "This is a annotation!"));
doc.newPage();
doc.add(new Paragraph("2 page"));
//给第二页添加批注
doc.add(new Annotation("Title", "This is a annotation!"));
doc.newPage();
doc.add(new Paragraph("3 page"));
//给第三页添加附件
Chunk chunk2 = new Chunk("\u00a0\u00a0"); // 必须有
PdfAnnotation annotation = PdfAnnotation.createFileAttachment(
instance, null, "Title", null,
FILE_DIR+"baidu.jpg",
"img.jpg");
annotation.put(PdfName.NAME,
new PdfString("Paperclip"));
chunk2.setAnnotation(annotation);
doc.add(chunk2);
doc.close();
在PDF中整个表格没有控制位置的方法 但是会始终在PDF中间,而且还是自适应,并且独占一行,
doc.open(); //打开PDF 默认为第一页
PdfPTable table = new PdfPTable(3);//设置3列
PdfPCell cell;
cell = new PdfPCell(new Phrase("Cell with colspan 3"));//设置列的内容
cell.setColspan(3);//合并3列
table.addCell(cell);//将内容添加到表格里第一行
cell = new PdfPCell(new Phrase("Cell with rowspan 2")); //设置列的内容
cell.setRowspan(2); //合并2行
table.addCell(cell); //将内容添加到表格里第二行
// 然后开始填空行
table.addCell("row 1; cell 1");
table.addCell("row 1; cell 2");
table.addCell("row 2; cell 1");
table.addCell("row 2; cell 2");
doc.add(table);
doc.close();
在上面代码的后面添加
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
PdfPTable table = new PdfPTable(3);//设置3列
PdfPCell cell;
cell = new PdfPCell(new Phrase("Cell with colspan 3"));//设置列的内容
cell.setColspan(3);//合并3列
table.addCell(cell);//将内容添加到表格里第一行
cell = new PdfPCell(new Phrase("Cell with rowspan 2")); //设置列的内容
cell.setRowspan(2); //合并2行
table.addCell(cell); //将内容添加到表格里第二行
// 然后开始填空行
table.addCell("row 1; cell 1");
table.addCell("row 1; cell 2");
table.addCell("row 2; cell 1");
table.addCell("row 2; cell 2");
doc.add(table);
//宽度100%
table.setWidthPercentage(100);
doc.add(table);
doc.add(new Paragraph("\n\n"));
//宽度50% 居左
table.setWidthPercentage(50);
table.setHorizontalAlignment(Element.ALIGN_LEFT);
doc.add(table);
doc.add(new Paragraph("\n\n"));
//宽度50% 居中
table.setWidthPercentage(50);
table.setHorizontalAlignment(Element.ALIGN_CENTER);
doc.add(table);
doc.add(new Paragraph("\n\n"));
//宽度50% 居右
table.setWidthPercentage(50);
table.setHorizontalAlignment(Element.ALIGN_RIGHT);
doc.add(table);
doc.add(new Paragraph("\n\n"));
//固定宽度 居中
table.setTotalWidth(300);
table.setLockedWidth(true);
table.setHorizontalAlignment(Element.ALIGN_CENTER);
doc.add(table);
doc.close();
//没有边框
PdfPTable table1 = new PdfPTable(3);
table1.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
table1.addCell(new Paragraph("Cell 1"));
table1.addCell(new Paragraph("Cell 2"));
table1.addCell(new Paragraph("Cell 3"));
document.add(table1);
doc.open(); //打开PDF 默认为第一页
//边框粗细颜色
doc.newPage();
Rectangle b1 = new Rectangle(0f, 0f);
b1.setBorderWidthLeft(6f);//左边框大小
b1.setBorderWidthBottom(5f);
b1.setBorderWidthRight(4f);
b1.setBorderWidthTop(2f);
b1.setBorderColorLeft(BaseColor.RED);//左边框
b1.setBorderColorBottom(BaseColor.ORANGE);//下边框
b1.setBorderColorRight(BaseColor.YELLOW);
b1.setBorderColorTop(BaseColor.GREEN);
PdfPTable table2 = new PdfPTable(1);
PdfPCell cell = new PdfPCell(new Paragraph("Cell 1"));
cell.cloneNonPositionParameters(b1);
table2.addCell(cell);
doc.add(table2);
doc.close();
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
PdfPTable table = new PdfPTable(3);
PdfPCell cell = new PdfPCell(new Paragraph("合并3列单元格",PdfFont.getChineseFont()));
cell.setColspan(3);//合并3个列单元格
table.addCell(cell);
table.addCell("1.1");
table.addCell("2.1");
table.addCell("3.1");
table.addCell("1.2");
table.addCell("2.2");
table.addCell("3.2");
cell = new PdfPCell(new Paragraph("红色边框",PdfFont.getChineseFont()));
//边框颜色
cell.setBorderColor(new BaseColor(255, 0, 0));
table.addCell(cell);
cell = new PdfPCell(new Paragraph("合并2列元格,背景灰色",PdfFont.getChineseFont()));
cell.setColspan(2);
//背景色
cell.setBackgroundColor(new BaseColor(0xC0, 0xC0, 0xC0));
table.addCell(cell);
table.setWidthPercentage(50); //设置表格的宽度
doc.add(new Paragraph("表格1",PdfFont.getChineseFont()));
doc.add(table);
doc.close();
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
PdfPTable table = new PdfPTable(3);
PdfPCell cell = new PdfPCell(new Paragraph("合并3列单元格",PdfFont.getChineseFont()));
cell.setColspan(3);//合并3个列单元格
table.addCell(cell);
table.addCell("1.1");
table.addCell("2.1");
table.addCell("3.1");
table.addCell("1.2");
table.addCell("2.2");
table.addCell("3.2");
doc.newPage();
doc.add(new Paragraph("表格前的间距",PdfFont.getChineseFont()));
table.setSpacingBefore(150f); //设置表格前的间距
table.setSpacingAfter(150f); //设置表格后的间距
doc.add(table);
doc.add(new Paragraph("表格后的间距",PdfFont.getChineseFont()));
doc.close();
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
doc.add(new Paragraph("按百分比设置单元格宽度\n\n",PdfFont.getChineseFont()));
float[] widths = {0.1f, 0.1f, 0.05f, 0.75f};
PdfPTable table = new PdfPTable(widths);
table.addCell("10%");
table.addCell("10%");
table.addCell("5%");
table.addCell("75%");
table.addCell("aa");
table.addCell("aa");
table.addCell("a");
table.addCell("aaaaaaaaaaaaaaa");
table.addCell("bb");
table.addCell("bb");
table.addCell("b");
table.addCell("bbbbbbbbbbbbbbb");
table.addCell("cc");
table.addCell("cc");
table.addCell("c");
table.addCell("ccccccccccccccc");
doc.add(table);
doc.add(new Paragraph("\n\n"));
doc.close();
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
PdfPTable table = new PdfPTable(2);
PdfPCell cell;
//设置高度
table.addCell(new PdfPCell(new Paragraph("任意高度",PdfFont.getChineseFont())));
cell = new PdfPCell(new Paragraph("1. blah blah\n2. blah blah blah\n3. blah blah\n4. blah blah blah\n5. blah blah\n6. blah blah blah\n7. blah blah\n8. blah blah blah"));
table.addCell(cell);
//固定高度
table.addCell(new PdfPCell(new Paragraph("固定高度",PdfFont.getChineseFont())));
cell.setFixedHeight(50f);
table.addCell(cell);
//最小高度
table.addCell(new PdfPCell(new Paragraph("最小高度",PdfFont.getChineseFont())));
cell = new PdfPCell(new Paragraph("最小高度:50",PdfFont.getChineseFont()));
cell.setMinimumHeight(50f);
table.addCell(cell);
//最后一行拉长到page底部
table.setExtendLastRow(true);
table.addCell(new PdfPCell(new Paragraph("拉长最后一行",PdfFont.getChineseFont())));
cell = new PdfPCell(new Paragraph("最后一行拉长到page底部",PdfFont.getChineseFont()));
table.addCell(cell);
doc.add(table);
doc.close();
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
//条形码
PdfContentByte cd = instance.getDirectContent();
Barcode128 code128 = new Barcode128();
code128.setCode("http://www.baidu.com".trim());
code128.setCodeType(Barcode128.CODE128);
Image code128Image = code128.createImageWithBarcode(cd, null, null);
code128Image.setAbsolutePosition(400,300);//条形码的位置
code128Image.scalePercent(125);
doc.add(code128Image);
//二维码
BarcodeQRCode qrcode = new BarcodeQRCode("http://www.baidu.com".trim(), 1, 1, null);
Image qrcodeImage = qrcode.getImage();
qrcodeImage.setAbsolutePosition(200,400);//二维码的位置
qrcodeImage.scalePercent(200);
doc.add(qrcodeImage);
doc.close();
公司提供具体模板格式你使用itext画出来…:
我这里随便从网上找来了一个发票
链接:https://pan.baidu.com/s/11iy8M0_Oo0E1tzEITMSSLw
提取码:1234
下面就来演示将上面这张图完整的还原出来,其实很简单将这张图为背景就行了,自己在需要添加值得地方设置变量就可以了
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
PdfWriter instance = PdfWriter.getInstance(doc, new FileOutputStream(FILE_DIR + "splitPDF2.pdf"));
doc.open(); //打开PDF 默认为第一页
Image img = Image.getInstance(FILE_DIR+"37.jpg");
img.scaleToFit(rect.getWidth(), rect.getHeight());
doc.add(img);
Phrase phrase1 = new Phrase("胡123",PdfFont.getChineseFont(12));
Phrase phrase2 = new Phrase("212346512312311",PdfFont.getChineseFont(12));
Phrase phrase3 = new Phrase("河南北京",PdfFont.getChineseFont(12));
PdfContentByte canvas = instance.getDirectContent();
// 参数1 默认就行 ,参数2默认就行,参数3内容,参数4x轴,参数5y轴,参数6字体旋转
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase1, 230, 550, 0);
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase2, 255, 530, 0);
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase3, 240, 510, 0);
doc.close();
这种方式的好处就是灵活,只需要提供画好的图片就行,
但是就是太麻烦了,需要自己控制文本的位置
常用于特别复杂的场景,使用word画不出来的场景,需要的样式花样特别多的场景,将样式效果脱落出去,我们只管文字的填充就行
需要的工具
本地word(windows10自带 或者 wps 都行)
1.
在线word转PDF
免费word转PDF https://smallpdf.com/cn/word-to-pdf 一天2次(足够用了)
免费word转PDF http://www.pdfdo.com/doc-to-pdf.aspx 没有次数限制
免费word转PDF http://wordtopdf.55.la/ 限制每次转换大小最多100m 没有次数限制
… 网上一大堆都是免费的如果上面都过期了那么自己找
1.
给PDF添加表单控件的工具 Adobe Acrobat DC
链接:https://pan.baidu.com/s/1dah5Gxh-YZRGFbZiCEVXFg
提取码:1234
解压后记得断网,不然就不能使用,然后找到目录里的AcroPro.msi 双击安装就行 之后就可以正常使用了
第一步先创建好word模板
利用上面提供的网站进行转换为PDF,转换完成后双击打开PDF(使用Adobe Acrobat DC打开的)
如果你右下角没有的话在更多工具里是有的,然后进到下面这个页面
之后会自动在空白的地方补充,文本框 如果没有自动补充那么在最上面有工具栏里自行添加
然后我们需要调整 文框的大小和位置自行调整最合适的位置,否则字默认显示位置是top,我们需要将文本框变小刚好够一个字的大小
字体的大小默认是跟随着文本框的高度进行变换的…
我们还可以点击某个文本右键属性设置文本格式(字体颜色 字体大小 ,自动计算…太多了基本HTML表单能实现的功能这里都能实现)
如果你要设置多个的话可以 shift+鼠标左键或者ctrl+a全部选中进行统一设置都行
调整后的效果
第二步使用java代码给文本框中对应的属性赋值
需要使用到3个关键的对象
PdfReader reader = new PdfReader(templateFile); // 模版文件目录
PdfStamper ps = new PdfStamper(reader, new FileOutputStream(outFile)); // 生成的输出流
AcroFields s = ps.getAcroFields(); //获取全部表单
案例:
public void editPdfTemplate(String templateFile, String outFile) throws IOException, DocumentException {
PdfReader reader = new PdfReader(templateFile); // 模版文件目录
PdfStamper ps = new PdfStamper(reader, new FileOutputStream(outFile)); // 生成的输出流
AcroFields s = ps.getAcroFields();
//编辑文本域表单的内容
s.setField("fill_1", "558");
s.setField("fill_2", "99966");
ps.setFormFlattening(true); // 模板中的变量赋值之后不能编辑
ps.close();
reader.close();
}
@Test
public void mob() throws DocumentException, IOException {
// 模板pdf 生成的pdf
editPdfTemplate(FILE_DIR+"moban.pdf",FILE_DIR+"wordPDF.pdf");
}
我们使用 freemarket+itext+springboot 方式
一般pdf是用来生成发票或者简单的报告用的, 而某些领导蛋疼非要使用PDF导出数据统计报表你说气不气,没办法都是打工的不解决不行啊…
网上有很多种方式但是我一 一都进行了实验最后得出了结论就是 别乱搞没用,基本都失真,HTML转PDF基本都没有不失真的,
而且html的定位…很多css都不支持…浪费了我2天的我时间把网上各种生成PDF都试了个遍,最后还是放弃了,发现这种方式不能实现特别复杂的html页面 只能实现一些需要大量数据报表的显示,而且对样式要求不高,一般遇到这种情况都是采用导出Excel了而不是导出PDF了.
而且这种方式没法手动添加页只能自动适配页,也就是利用模板语法</#list xxx >
,进行循环.
每页大具体大小是可以控制的多大都行
但是必须要保证每次循环的内容必须都是在一页中. 之后每循环一次就是一页
我们HTML模板使用的是 Freemarker 也就是需要一些模板语法懂得都懂不会上百度…
简单来说就只需要3种语法就足够了:
获取值
${name}
if
<#if var ??>
不是空
<#else>
为空
</#if>
循环
<#list goodsList as goods>
姓名: ${goods.name} 年龄: ${goods.age} 性别${goods.sex} <br/>
</#list>
我们还需要知道这种方式的弊端
css 各种定位 和 浮动 弹性布局 … 都不能使用 那么如何控制属性的位置呢?? 可以通过表格的方式 表格内地元素都能使用的.
比如 text-align 控制文本位置 valign 垂直对齐 align 水平对齐 , 行高 … 都可以进行控制 自己慢慢试试就行了
1.
图片不能加载本地图片 只能加载服务器图片, 也就是http请求的图片 如果想加载本地图片需要使用nginx做本地文件代理
location /images/ {
alias D:/nginx/nginx-1.19.10/html/images/; # 代理的路径
autoindex on; #是否开启目录浏览
}
当访问http://xxxx:80/images
就相当于在访问 D:/nginx/nginx-1.19.10/html/images/
使用的话我们只需要在后面在追加一个图片名称就行了 http://xxxx:80/images/1.jpg
1.
对于空值的校验,Freemarker 在渲染模板时不允许取值为空,如果某一项值可能为空,则必须添加校验,推荐取值方式
<#if demoValue??>
${demoValue}
</#if>
模板样式必须添加的
/*-------------- 必须有的内容-------------------------*/
/* 调整生成PDF页面宽和高 */
@page {
size: 340mm 350mm;
}
body{
margin: 0;
padding: 0;
font-family: SimSun;
}
table {
page-break-inside: auto;
-fs-table-paginate: paginate;
border-spacing: 0;
cellspacing: 0;
cellpadding: 0;
border: solid 1px #ccc;
padding: 2px 2px;
}
tr {
page-break-inside: avoid;
page-break-after: auto;
}
/*---------------必须有的内容------------------------*/
上代码就是防止生成的PDF时候表格内容超过当前页的时候不能自动适配
下面就详细演示所有代码 (web版下载)
项目结构
宋体字体
链接:https://pan.baidu.com/s/15uLOVmh2NZHG3g2zoPoaKw
提取码:1234
html模板
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<title>Title</title>
<style>
/*-------------- 必须有的内容-------------------------*/
/* 调整页面宽和高 */
@page {
size: 340mm 350mm;
}
body{
margin: 0;
padding: 0;
font-family: SimSun;
}
table {
page-break-inside: auto;
-fs-table-paginate: paginate;
border-spacing: 0;
cellspacing: 0;
cellpadding: 0;
border: solid 1px #ccc;
padding: 2px 2px;
}
tr {
page-break-inside: avoid;
page-break-after: auto;
}
/*---------------必须有的内容------------------------*/
table{
border: 1px solid #333;
border-bottom: none;
border-left: none;
}
td{
height: 30px;
border: 1px solid #333;
border-top: none;
text-align: center;
}
tr.title{
font-weight: bold;
}
td.title{
height: 50px;
font-weight: bold;
}
td.value{
color: blue;
}
td.content{
font-size: 12px;
text-align: left;
}
td.sign{
text-align: left;
height: 40px;
}
</style>
</head>
<body>
<#list 1..max as i>
<table class="table" cellspacing="0">
<tr >
<td class="title" colspan="10">
项目成员绩效考核表
</td>
</tr>
<tr >
<td style="width: 10%;" align="">
被考核者
</td>
<td class="value" style="width: 10%;">
<#if employee??>
<#if employee.name??>
${employee.name}
</#if>
</#if>
</td>
<td style="width: 10%;">
部门
</td>
<td colspan="2" class="value" style="width: 20%;">
<#if employeeDepart??>
<#if employeeDepart.name??>
${employeeDepart.name}
</#if>
</#if>
</td>
<td style="width: 10%;">
考核者
</td>
<td class="value" style="width: 10%;">
<#if acceptUser??>
<#if acceptUser.name??>
${acceptUser.name}
</#if>
</#if>
</td>
<td style="width: 10%;">
考核时间
</td>
<td colspan="2" class="value" style="width: 20%;">
<#if statisticalTime??>
${statisticalTime}
</#if>
</td>
</tr>
<tr >
<td colspan="10">
第一部分工作目标(权重80%)
</td>
</tr>
<tr class="title">
<td colspan="2">
指标名称
</td>
<td >
权重
</td>
<td colspan="3">
指标定义与评分标准
</td>
<td >
完成值
</td>
<td >
数据提供部门/人
</td>
<td >
自评得分
</td>
<td >
上级评分
</td>
</tr>
<tr >
<td colspan="2">
工作计划完成率
</td>
<td >
30%
</td>
<td colspan="3" class="content">
实际完成量/计划完成量*100%<br/>
1.完成比≥100%,本项为满分;<br/>
2.完成比在90%(含)-100%(不含),扣10分;<br/>
3.完成比在80%(含)-90%(不含),扣20分;<br/>
4.完成比在80%(不含)以下的,本项为0分
</td>
<td class="value">
<#if jobCompletionRate??>
${jobCompletionRate*100}%
</#if>
</td>
<td >
项目经理
</td>
<td class="value">
<#if jobCompletionRateScore??>
${jobCompletionRateScore}
</#if>
</td>
<td class="value">
<#if jobCompletionRateSuperiorScore??>
${jobCompletionRateSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
工作计划完成及时率
</td>
<td >
25%
</td>
<td colspan="3" class="content">
实际完成天数/计划完成天数*100%<br/>
1.完成比≦100%,本项为满分;<br/>
2.完成比在100%-110%(不含),扣5分;<br/>
3.完成比在110%(含)-130%(不含)扣10分;<br/>
4.完成比在130%(含)以上的,本项为0分
</td>
<td class="value">
<#if finishRate??>
${finishRate*100}%
</#if>
</td>
<td >
项目经理
</td>
<td class="value">
<#if finishRateScore??>
${finishRateScore}
</#if>
</td>
<td class="value">
<#if finishRateSuperiorScore??>
${finishRateSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
返工率
</td>
<td >
20%
</td>
<td colspan="3" class="content">
实际返工次数/计划返工次数*100%<br/>
1.完成比≤100%,本项为满分;<br/>
2.完成比在100%(不含)-110%(不含),扣10分;<br/>
3.完成比在110%(不含)-120%(含),扣15分;<br/>
4.完成比在120%(不含)以上的,本项为0分
</td>
<td class="value">
<#if returnRate??>
${returnRate*100}%
</#if>
</td>
<td >
项目经理<br/>
外型供应商
</td>
<td class="value">
<#if returnRateScore??>
${returnRateScore}
</#if>
</td>
<td class="value">
<#if returnRateSuperiorScore??>
${returnRateSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
技术文档资料保存完整性
</td>
<td >
15%
</td>
<td colspan="3" class="content">
实际上传资料数/要求上传资料数*100%<br/>
1.完成比100%,本项得满分;<br/>
2.完成比<100%,本项为0分
</td>
<td class="value">
<#if uploadRate??>
${uploadRate*100}%
</#if>
</td>
<td >
项目经理
</td>
<td class="value">
<#if uploadRateScore??>
${uploadRateScore}
</#if>
</td>
<td class="value">
<#if uploadRateSuperiorScore??>
${uploadRateSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
满意度
</td>
<td >
10%
</td>
<td colspan="3" class="content">
及时参加项目例会、积极汇报工作<br/>
总分10分<br/>
每遗漏一次,扣4分;扣完为止
</td>
<td >
--
</td>
<td >
项目经理
</td>
<td class="value">
<#if satisfactionScore??>
${satisfactionScore}
</#if>
</td>
<td class="value">
<#if satisfactionSuperiorScore??>
${satisfactionSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="10">
第二部分工作态度(权重20%)
</td>
</tr>
<tr class="title">
<td colspan="2">
工作态度指标
</td>
<td>
衡量方法
</td>
<td colspan="4">
衡量标准
</td>
<td >
权重
</td>
<td >
自评得分
</td>
<td >
上级评分
</td>
</tr>
<tr >
<td colspan="2">
责任心
</td>
<td>
上级评价
</td>
<td colspan="4" class="content">
1.仅仅能按上级要求完成本职工作(5分); <br/>
2.能够严格按照工作标准完成工作目标对本职工作负责到底,<br/>
工作中不推卸责任、不上交矛盾,失误较少(10分); <br/>
3.对待工作不怕繁琐、有耐心,考虑问题与做事细致、周到(15分); <br/>
4.对待工作精益求精,力求一次性做到完美(20分); <br/>
5.对团队成员拥有强烈的责任感,努力帮助团队成员提升工作质量(25分)
</td>
<td >
25%
</td>
<td class="value">
<#if responsibilityScore??>
${responsibilityScore}
</#if>
</td>
<td class="value">
<#if responsibilitySuperiorScore??>
${responsibilitySuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
主动性
</td>
<td>
上级评价
</td>
<td colspan="4" class="content">
1.按上级安排/ 指示做事,安排什么做什么(5分)<br/>
2.按自己的职位职责做事,工作任务大多能完成;<br/>
同时对工作中出现的问题,也能被动反应,予以处理(10分)<br/>
3.对自己的工作大致有思考,上级安排的任务能有效配合确定工作计划,<br/>
并按计划完成工作任务;同时能积极处理工作中出现的各种问题,<br/>
需要请示或上级支持时也能按程序办理(15分)<br/>
4.提前思考、主动安排自己的工作计划,并将之主动与上级沟通、协商、确定,<br/>
按计划推进、完成工作任务;对工作问题提前预防,并妥善处理各类问题;<br/>
能积极主动地协助同事完成职责范围外的其他工作(20分)<br/>
5.上级只给出一个方向或任务,既能独立地制定计划、组织资源、推进实施、保证完成,<br/>
支持、鼓励团队成员与周围同事积极主动开展工作,<br/>
能营造积极、主动的文化氛围(25)
</td>
<td >
25%
</td>
<td class="value">
<#if initiativeScore??>
${initiativeScore}
</#if>
</td>
<td class="value">
<#if initiativeSuperiorScore??>
${initiativeSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
团队合作
</td>
<td>
上级评价
</td>
<td colspan="4" class="content">
1.积极融入团队并乐于接受同事帮助,配合团队完成工作(5分)<br/>
2.主动给予同事必要的帮助;碰到困难时,善于利用团队的力量解决问题(10分)<br/>
3.决策前积极发表个人意见,充分参与团队讨论;决策后,个人无论是否有异议,<br/>
必须从行动上完全予以支持(15分);<br/>
4.能够客观认识同事的优缺点,并在工作中充分体现“对事不对人”的原则(20分)<br/>
5.能够以积极正面的心态去影响团队,并改善团队表现和氛围(25分)<br/>
</td>
<td >
25%
</td>
<td class="value">
<#if teamCooperationScore??>
${teamCooperationScore}
</#if>
</td>
<td class="value">
<#if teamCooperationSuperiorScore??>
${teamCooperationSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
保密意识
</td>
<td>
上级评价
</td>
<td colspan="4" class="content">
1.对岗位的保密责任有一定的认识(5分);<br/>
2.熟悉公司保密协议,明确职责范围内的保密事项,并采取相应的维护措施(10分)<br/>
3.以身作则,自觉、严格遵守保密协议,对保密协议未明确界定的问题能够很好的处理(15分)<br/>
4.影响身边的同事,宣传保密意识,随时提醒同事;发现保密协议的缺陷和漏洞<br/>
能及时向有关部门报告,并提出完善建议(20分);<br/>
5.获悉他人违反和破坏保密协议时,积极抵制,能够及时向公司有关部门报告,<br/>
并分情况采取积极措施以最大限度减少恶性后果,处理得当(25分)
</td>
<td >
25%
</td>
<td class="value">
<#if secrecyScore??>
${secrecyScore}
</#if>
</td>
<td class="value">
<#if secrecySuperiorScore??>
${secrecySuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="8">
合计
</td>
<td class="value">
<#if totalScore??>
${totalScore}
</#if>
</td>
<td class="value">
<#if totalSuperiorScore??>
${totalSuperiorScore}
</#if>
</td>
</tr>
<tr >
<td colspan="2">
等级评定规则
</td>
<td class="content" colspan="5">
A:优秀(100分以上)<br/>
B:良好(90-100分)<br/>
C:合格(80-90分)<br/>
D:基本合格(70-80分)<br/>
E:需改进(60-70分)<br/>
F:不合格(60分以下)
</td>
<td >
等级评定
</td>
<td class="value" colspan="2">
<#if grade??>
${grade}
</#if>
</td>
</tr>
<tr style="height: 100px;" >
<td colspan="2">
自我总结
</td>
<td colspan="8" class="content value">
<#if selfSummary??>
${selfSummary}
</#if>
</td>
</tr>
<tr >
<td rowspan="2" colspan="2">
考核结果确认
</td>
<td class="sign" colspan="4">
考核者签名:
</td>
<td class="sign" colspan="4">
日期:
</td>
</tr>
<tr>
<td class="sign" colspan="4">
被考核者签名:
</td>
<td class="sign" colspan="4">
日期:
</td>
</tr>
</table>
</#list>
</body>
</html>
获取资源路径工具类
package com.pdf.utils;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
/** * @Description: 项目静态资源文件工具类 * 仅可用于包含在web项目中的资源文件路径,资源文件必须放置于 web 模块下 */
public class ResourceFileUtil {
/** * 获取资源文件 * * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名) * eg: "templates/pdf_export_demo.ftl" * @return * @throws FileNotFoundException */
public static File getFile(String relativePath) throws FileNotFoundException {
if (relativePath == null || relativePath.length() == 0) {
return null;
}
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX
+ relativePath);
return file;
}
/** * 获取资源绝对路径 * * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名) * eg: "templates/pdf_export_demo.ftl" * @return * @throws FileNotFoundException */
public static String getAbsolutePath(String relativePath) throws FileNotFoundException {
return getFile(relativePath).getAbsolutePath();
}
/** * 获取资源父级目录 * * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名) * eg: "templates/pdf_export_demo.ftl" * @return * @throws FileNotFoundException */
public static String getParent(String relativePath) throws FileNotFoundException {
return getFile(relativePath).getParent();
}
/** * 获取资源文件名 * * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名) * eg: "templates/pdf_export_demo.ftl" * @return * @throws FileNotFoundException */
public static String getFileName(String relativePath) throws FileNotFoundException {
return getFile(relativePath).getName();
}
}
生成PDF工具类
package com.pdf.utils;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Entities;
import org.jsoup.select.Elements;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.util.Map;
public class PDFUtil {
// 必须在resoures下面
private static String font="templates/font/simsun.ttf"; //字体
private static String templ="templates/pdf_export_employee_kpi.html"; //模板
private PDFUtil(){}
private volatile static Configuration configuration;
static {
if (configuration == null) {
synchronized (PDFUtil.class) {
if (configuration == null) {
configuration = new Configuration(Configuration.VERSION_2_3_28);
}
}
}
}
/** * freemarker 引擎渲染 html * * @param dataMap 传入 html 模板的 Map 数据 * @param ftlFilePath html 模板文件相对路径(相对于 resources路径,路径 + 文件名) * eg: "templates/pdf_export_demo.ftl" * @return */
public static String freemarkerRender(Map<String, Object> dataMap, String ftlFilePath) {
Writer out = new StringWriter();
configuration.setDefaultEncoding("UTF-8");
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
try {
configuration.setDirectoryForTemplateLoading(new File(ResourceFileUtil.getParent(ftlFilePath)));
configuration.setLogTemplateExceptions(false);
configuration.setWrapUncheckedExceptions(true);
Template template = configuration.getTemplate(ResourceFileUtil.getFileName(ftlFilePath));
template.process(dataMap, out);
out.flush();
return out.toString();
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/** * 使用 iText 生成 PDF 文档 * * @param htmlTmpStr html 模板文件字符串 * @param fontFile 所需字体文件(相对路径+文件名) * */
public static byte[] createPDF(String htmlTmpStr, String fontFile) {
ByteArrayOutputStream outputStream = null;
byte[] result = null;
try {
outputStream = new ByteArrayOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(htmlTmpStr);
ITextFontResolver fontResolver = renderer.getFontResolver();
// 解决中文支持问题,需要所需字体(ttc)文件
fontResolver.addFont(ResourceFileUtil.getAbsolutePath(fontFile),BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
renderer.layout();
renderer.createPDF(outputStream);
result=outputStream.toByteArray();
if(outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/** * PDF 文件导出 * * @return */
public static ResponseEntity<?> export(Map<String, Object> dataMap, String pdfName) {
HttpHeaders headers = new HttpHeaders();
/** * 数据导出(PDF 格式) */
String htmlStr = PDFUtil.freemarkerRender(dataMap, templ);
String s = formatHtml(htmlStr);
System.out.println(s);
byte[] pdfBytes = PDFUtil.createPDF(s, font);
if (pdfBytes != null && pdfBytes.length > 0) {
String fileName=null;
if (pdfName!=null){
fileName= pdfName;
}else {
fileName = System.currentTimeMillis() + (int) (Math.random() * 90000 + 10000) + ".pdf";
}
headers.setContentDispositionFormData("attachment", fileName);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(pdfBytes, headers, HttpStatus.OK);
}
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
headers, HttpStatus.NOT_FOUND);
}
public static ResponseEntity<?> export(Map<String, Object> dataMap) {
return export( dataMap ,null);
}
/** * 使用jsoup规范化html * * @param html html内容 * @return 规范化后的html */
private static String formatHtml(String html) {
Document doc = Jsoup.parse(html);
// 去除过大的宽度
String style = doc.attr("style");
if (StringUtils.isNotEmpty(style) && style.contains("width")) {
doc.attr("style", "");
}
Elements divs = doc.select("div");
for (Element div : divs) {
String divStyle = div.attr("style");
if (StringUtils.isNotEmpty(divStyle) && divStyle.contains("width")) {
div.attr("style", "");
}
}
// jsoup生成闭合标签
doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml);
doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
return doc.html();
}
}
Controller
package com.pdf.controller;
import com.pdf.utils.PDFUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@RestController
public class PdfController {
/** * PDF 文件导出 */
@GetMapping(value = "/pdf")
public ResponseEntity<?> export(){
try {
Map<String, Object> dataMap = new HashMap<>(16);
dataMap.put("statisticalTime",new Date().toString());
dataMap.put("max",5);
ResponseEntity<?> responseEntity = PDFUtil.export(dataMap);
return responseEntity;
} catch (Exception e) {
e.printStackTrace();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
headers, HttpStatus.NOT_FOUND);
}
}
启动类
package com.pdf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "com")
public class ApplictioBoot {
public static void main(String[] args) {
SpringApplication.run(ApplictioBoot.class,args);
}
}
然后进行测试 访问 http://localhost:8080/pdf
注意:如果上面 word 模板方式或者 html+freemaret模板方式都不满足你的业务需求的话那么,你就需要手写Itext的方式进行生成PDF了
有两种方式
我们常用于第二种方式 ,而第一种方式只需要会io流就行了这里就不多讲了,直接上第二种方式的代码就行了
package com.pdf.utils;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
public class PdfDownLoadUtils {
public static ByteArrayOutputStream ini() throws DocumentException {
Rectangle rect = new Rectangle(PageSize.B4.rotate());
Document doc = new Document(rect);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter instance = PdfWriter.getInstance(doc, baos);
doc(doc);
return baos;
}
private static void doc(Document doc) throws DocumentException {
doc.open(); //打开PDF 默认为第一页
PdfPTable table = new PdfPTable(3);//设置3列
PdfPCell cell;
cell = new PdfPCell(new Phrase("Cell with colspan 3"));//设置列的内容
cell.setColspan(3);//合并3列
table.addCell(cell);//将内容添加到表格里第一行
cell = new PdfPCell(new Phrase("Cell with rowspan 2")); //设置列的内容
cell.setRowspan(2); //合并2行
table.addCell(cell); //将内容添加到表格里第二行
// 然后开始填空行
table.addCell("row 1; cell 1");
table.addCell("row 1; cell 2");
table.addCell("row 2; cell 1");
table.addCell("row 2; cell 2");
doc.add(table);
doc.close();
}
}
核心代码
/** * PDF 文件导出 */
@GetMapping(value = "/pdfDow")
public ResponseEntity<?> export1(HttpServletRequest request, HttpServletResponse response) {
HttpHeaders headers = new HttpHeaders();
String pdName="student.pdf"; //文件名称
//设置页面编码格式
try {
ByteArrayOutputStream ini = PdfDownLoadUtils.ini();
// 页面打开PDF 不下载
// response.setContentLength(ini.size());
// OutputStream out = response.getOutputStream();
// ini.writeTo(out);
// return new ResponseEntity<byte[]>( headers, HttpStatus.OK);
// 下载PDF
headers.setContentDispositionFormData("attachment", pdName);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(ini.toByteArray(), headers, HttpStatus.OK);
} catch (DocumentException e) {
e.printStackTrace();
}
// 返回错误信息
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
headers, HttpStatus.NOT_FOUND);
}
如果非要使用第一种方式的话只需要下面这一个方法就行了
public static void downloadFile(String path, HttpServletResponse response) {
try {
// path是指欲下载的文件的路径。
File file = new File(path);
// 取得文件名。
String filename = file.getName();
// 以流的形式下载文件。
InputStream fis = new BufferedInputStream(new FileInputStream(path));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "utf-8"));
response.addHeader("Content-Length", "" + file.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
/** * PDF 文件导出 */
@GetMapping(value = "/pdfDow")
public ResponseEntity<?> export1(HttpServletRequest request, HttpServletResponse response){
try {
int a=1/0;
PdfDownLoadUtils.downloadFile(ResourceFileUtil.getAbsolutePath("PDF/student.pdf"),response);
} catch (Exception e) {
e.printStackTrace();
}
HttpHeaders headers = new HttpHeaders();
// 失败返回错误信息
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
headers, HttpStatus.NOT_FOUND);
}
table.addCell("row 2; cell 1");
table.addCell("row 2; cell 2");
doc.add(table);
doc.close();
}
}
核心代码
/** * PDF 文件导出 */
@GetMapping(value = "/pdfDow")
public ResponseEntity<?> export1(HttpServletRequest request, HttpServletResponse response) {
HttpHeaders headers = new HttpHeaders();
String pdName="student.pdf"; //文件名称
//设置页面编码格式
try {
ByteArrayOutputStream ini = PdfDownLoadUtils.ini();
// 页面打开PDF 不下载
// response.setContentLength(ini.size());
// OutputStream out = response.getOutputStream();
// ini.writeTo(out);
// return new ResponseEntity<byte[]>( headers, HttpStatus.OK);
// 下载PDF
headers.setContentDispositionFormData("attachment", pdName);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(ini.toByteArray(), headers, HttpStatus.OK);
} catch (DocumentException e) {
e.printStackTrace();
}
// 返回错误信息
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
headers, HttpStatus.NOT_FOUND);
}
如果非要使用第一种方式的话只需要下面这一个方法就行了
public static void downloadFile(String path, HttpServletResponse response) {
try {
// path是指欲下载的文件的路径。
File file = new File(path);
// 取得文件名。
String filename = file.getName();
// 以流的形式下载文件。
InputStream fis = new BufferedInputStream(new FileInputStream(path));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "utf-8"));
response.addHeader("Content-Length", "" + file.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
/** * PDF 文件导出 */
@GetMapping(value = "/pdfDow")
public ResponseEntity<?> export1(HttpServletRequest request, HttpServletResponse response){
try {
int a=1/0;
PdfDownLoadUtils.downloadFile(ResourceFileUtil.getAbsolutePath("PDF/student.pdf"),response);
} catch (Exception e) {
e.printStackTrace();
}
HttpHeaders headers = new HttpHeaders();
// 失败返回错误信息
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
headers, HttpStatus.NOT_FOUND);
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_45203607/article/details/120249035
内容来源于网络,如有侵权,请联系作者删除!