唉,搜个解决方式真难,网上各种版本,各种复制,关键用着还不爽,真有意思。
只好自己研究了,希望有个只需要关注业务代码的就好了。
底层类我放在了最下面。
不想说话,直接上代码吧、
maven版本
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
仅导出页码code
这种只需要关注自己的业务代码就可以了,
底层类复制后,直接在writeBody中写自己的逻辑代码就好了
import com.alibaba.fastjson.JSONObject;
import com.itextpdf.text.Chapter;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.Section;
import com.itextpdf.text.pdf.PdfPTable;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* {
* "title":"班级表",
* "students":[
* {
* "id":1,
* "name":"小明",
* "age":12,
* "remark":"小明小明一把手雷弹"
* },
* {
* "id":2,
* "name":"小红",
* "age":12,
* "remark":"小红小红快来我加玩"
* }
* ],
* "teachers":[
* {
* "id":1,
* "name":"小铭",
* "age":22,
* "remark":"这老师是教...你猜"
* },
* {
* "id":2,
* "name":"小鸣",
* "age":23,
* "remark":"对不起,我只负责打孩纸"
* }
* ]
* }
*/
@Slf4j
public class TestPdfView extends AbstractPdfView {
private JSONObject json;
private int chapterSeq = 1; //书签序号
public TestPdfView(JSONObject json) {
this.json = json;
}
@Override
protected void writeBody() throws Exception {
buildTitle();
//正文开始
document.newPage();
buildTeacher();
buildStudent();
}
private void buildTitle() throws Exception {
String title = json.getString("title");
document.addTitle(title);
document.newPage();
PdfUtil.setLogo(document, title);
}
private void buildTeacher() throws Exception {
Chapter chapter = getChapter("教师列表");
List<JSONObject> teachers = json.getJSONArray("teachers").toJavaList(JSONObject.class);
for (JSONObject j : teachers) {
Section section = addSection(chapter, getKey(j,"name"));
PdfPTable table = PdfUtil.getBorderTable(4, Rectangle.BOX);
table.addCell(addFont10(getKey(j, "id")));
table.addCell(addFont10(getKey(j, "name")));
table.addCell(addFont10(getKey(j, "age")));
table.addCell(addFont10(getKey(j, "remark")));
document.add(table);
}
}
private void buildStudent() throws Exception {
Chapter chapter = getChapter("学生列表");
List<JSONObject> students = json.getJSONArray("students").toJavaList(JSONObject.class);
for (JSONObject j : students) {
Section section = addSection(chapter, getKey(j,"name"));
PdfPTable table = PdfUtil.getBorderTable(4, Rectangle.BOX);
table.addCell(addFont10(getKey(j, "id")));
table.addCell(addFont10(getKey(j, "name")));
table.addCell(addFont10(getKey(j, "age")));
table.addCell(addFont10(getKey(j, "remark")));
document.add(table);
}
}
private Chapter getChapter(String title) throws Exception {
Chapter chapter = new Chapter(addFont12(title), chapterSeq++);
chapter.setTriggerNewPage(false);
chapter.setNumberStyle(Section.NUMBERSTYLE_DOTTED);
document.add(chapter);
return chapter;
}
private Section addSection(Chapter chapter, String title) throws Exception {
Section section = chapter.addSection(addFont12(title));
section.setIndentationLeft(8f);
section.setNumberStyle(Section.NUMBERSTYLE_DOTTED);
document.add(section);
return section;
}
private String getKey(JSONObject j, String key) {
return String.valueOf(j.get(key));
}
}
效果图
image.png
image.png
导出目录
这个只关注业务代码其实也可以搞,麻烦些也可以实现,但我这人有个优点:懒,蟹蟹。
导出目录比较麻烦:试过几种方法,最终思路:
1.先导出正文,得到目录结构与其对应页码的缓存
2.导出目录到新的document中,得到目录占用的页数
3.合并俩个document,此时目录缓存的页码=原页码+目录总页码
注:该方法不支持点击目录跳转页码,所以可以使用标签来进行定位。
所以,标签中的值也需要重新计算。
不想说话,不想上代码,啊哈哈哈
效果图
image.png
image.png
image.png
好吧,代码不复杂,须重写抽象类的finish方法:
1.获取目录+标题应占用的页数
2.创建目录页(注意目录最后的页数应该是多少)
3.调用父类finish方法(注意reader的顺序)
因目录不支持点击跳转,所以替代方案使用标签
效果图
所以,如果要搞一个只需要关注业务逻辑的话,思路应该是:
1.有个缓存用于存放目录缓存
2.有个抽象标题方法,用户自定义标题,仅支持一页
3.添加一个根据目录缓存构造目录的方法
4.改造finish方法:合并标题、目录与正文,此处应重写目录对应的页码
5.欢迎大家试着写一下,最好留言给个链接,嘿嘿嘿嘿
工具类PdfUtil:
我喜欢所有排版都使用table,主要是简单方便还好用
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPTable;
import org.springframework.core.io.ClassPathResource;
public class PdfUtil {
private static final int A4_WIDTH = 595;
private static final int A4_HEIGHT = 842;
private static final int MARGIN_TOP = 36;
private static final int MARGIN_BOTTOM = 36;
private static final int MARGIN_LEFT = 57;
private static final int MARGIN_RIGHT = 57;
public static BaseFont getBaseFont() {
try {
return BaseFont.createFont("/TTF/NotoSansCJKsc-Regular.otf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
} catch (Exception e) {
throw new MsgException("");
}
}
public static Font getFont(float fontSize) {
return new Font(getBaseFont(), fontSize, Font.NORMAL);
}
public static PdfPTable getNoBorderTable() {
return getBorderTable(1, Rectangle.NO_BORDER);
}
public static PdfPTable getBorderTable(int numColumns, int border) {
PdfPTable table = new PdfPTable(numColumns);
table.setSpacingBefore(4f);
table.setSpacingAfter(4f);
table.setTotalWidth(getTableWidth());
table.setLockedWidth(true);
table.getDefaultCell().setBorder(border);
table.getDefaultCell().setMinimumHeight(24f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
return table;
}
/**
* 上面是图标,下面是pdf标题名称
*/
public static void setLogo(Document document, String name) throws Exception {
Image logo = Image.getInstance(new ClassPathResource("/pdf/logo.png").getURL());
PdfPTable t = getNoBorderTable();
t.getDefaultCell().setVerticalAlignment(Element.ALIGN_BOTTOM);
t.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
t.getDefaultCell().setFixedHeight(150f);//表格固定行高
t.addCell(logo);
t.addCell(new Paragraph(name, getFont(16f)));
document.add(t);
}
public static float getTableWidth() {
return A4_WIDTH - MARGIN_LEFT - MARGIN_RIGHT;
}
}
公用抽象类
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractPdfView {
private final Logger log = LoggerFactory.getLogger(AbstractPdfView.class);
protected Document document;
protected PdfWriter writer;
protected final int size = 2048;
protected Font font8 = PdfUtil.getFont(8f); //页码用
protected Font font10 = PdfUtil.getFont(10f); //正文用
protected Font font12 = PdfUtil.getFont(12f); //标题用
public void start(HttpServletResponse response) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
document = new Document(PageSize.A4);
writer = PdfWriter.getInstance(document, baos);
prepare();
document.open();
writeBody();
document.close();
List<PdfReader> readers = new ArrayList<>();
readers.add(new PdfReader(baos.toByteArray()));
baos = finish(readers);
// flush to http response.
flushStream(baos, response);
} catch (Exception e) {
log.error("导出pdf文档失败", e);
throw new MsgException("导出pdf文档失败");
}
}
/**
* 添加监听事件请重写该方法
* 如:writer.setPageEvent(PageHelper...),这种无须finish方法
*/
protected void prepare() throws Exception {
writer.setViewerPreferences(PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage);
}
protected abstract void writeBody() throws Exception;
/**
* 完成pdf,写入页码
*/
protected ByteArrayOutputStream finish(List<PdfReader> readers) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
Document doc = new Document(PageSize.A4);
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
int currentPage = 1, totalPage = readers.stream().mapToInt(PdfReader::getNumberOfPages).sum();
for (PdfReader reader : readers) {
PdfImportedPage page;
PdfCopy.PageStamp stamp;
int pages = reader.getNumberOfPages();
for (int index = 1; index <= pages; index++) {
doc.newPage();
page = copy.getImportedPage(reader, index);
stamp = copy.createPageStamp(page);
ColumnText.showTextAligned(stamp.getUnderContent(), Element.ALIGN_CENTER,
new Phrase(addFont8(String.format("第%d页,共%d页", currentPage++, totalPage))), 300f, 16f, 0f);
stamp.alterContents();
copy.addPage(page);
}
reader.close();
}
doc.close();
copy.close();
return out;
}
private void flushStream(ByteArrayOutputStream baos, HttpServletResponse response) throws Exception {
response.reset();
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Content-disposition", "inline; filename=" + RandomUtil.uuid() + ".pdf");
response.setCharacterEncoding(Charset.forName("UTF-8").name());
response.setContentType(MediaType.APPLICATION_PDF_VALUE);
response.setContentLength(baos.size());
ServletOutputStream out = response.getOutputStream();
baos.writeTo(out);
out.flush();
}
protected Paragraph addFont8(String content) {
return addText(content, font8);
}
protected Paragraph addFont10(String content) {
return addText(content, font10);
}
protected Paragraph addFont12(String content) {
return addText(content, font12);
}
private Paragraph addText(String content, Font font) {
Paragraph paragraph = new Paragraph(content, font);
paragraph.setAlignment(Element.ALIGN_LEFT);
return paragraph;
}
}
对了,logo.png是放在resources里面的, 我直接截的百度图
字体呢,我用的是思源宋体,大家自己搜索,随便下好了。
有些地方没有给,一些大家直接就能猜到,一些就是我故意的,就是留那么一手防复制党,就是辣么jian,呵。
拷过去只需要关注pdf的正文代码和样式代码就好了
网友评论