项目需要样式比较复杂,刚开始因为时间比较紧所以采用了Itextpdf插件代码生成pdf ,实话说过程十分繁琐,因为pdf文件样式比较多,表格也比较多,各种的表格,还有就是页眉页脚页码都要自己找页面位置坐标但是又不能像html那样方便更改查看样式,只能改一点导出来看看合适不,然后再改再导出来看。
私下调研了很多,现在罗列一下我所用到的一些样式处理,希望可以帮助需要做这个功能的朋友少踩坑,少尝试,直接出完美pdf报告。
//定义 页面大小,以及页边距左右上下
package com.example.demo.controller;
import com.example.demo.config.PDFConfig;
import com.example.demo.utils.PDFBuilder;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.draw.LineSeparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author hongli.zhang
* @create 2020/5/26 13:02
**/
@RestController
@RequestMapping("/businessApplication/")
public class PdfContrller {
@Autowired
PDFConfig config;//这个是配置类,里面设置了需要用到的字体和logo图片的绝对路径
public static void setImg(PdfWriter writer, String path, float fitWidth, float fitHeight, float absoluteX, float absoluteY) {
try {
Image image = Image.getInstance(path);
image.setAlignment(Image.MIDDLE);
image.scaleToFit(fitWidth, fitHeight);
image.setAbsolutePosition(absoluteX, absoluteY);
writer.getDirectContent().addImage(image);
}catch(Exception e){
}
}
@GetMapping("/test")
public void test(HttpServletRequest requ, HttpServletResponse resp) throws DocumentException {
//设置页面大小为A4纸大小,以及页边距左右上下
Document document = new Document(PageSize.A4, 100F, 100F, 80F, 120F);
PdfWriter writer = null;
try {
FileOutputStream out = new FileOutputStream("E:\\new.pdf");
writer = PdfWriter.getInstance(document, out);
} catch ( IOException | DocumentException e) {
e.printStackTrace();
}
// 定义页眉和页脚页码事件,PDFBuilder代码在后面
PDFBuilder builder = new PDFBuilder();
//设置页面监听事件
writer.setPageEvent(builder);
document.open();
//换行
Paragraph newLine = new Paragraph("\n");
float tableWidthPercentage = 100f; //表格的整体宽度
//表格背景色
BaseColor green = new BaseColor(175, 215, 136);
BaseColor blue = new BaseColor(148, 170, 214);
//图标
setImg(writer, config.getLogo(), 130, 100, document.getPageSize().getRight(230), 700);
//所需字体
String fontPath = config.getCalibri();
String fontBlodPath = config.getCalibrib();
BaseFont bf = null;
BaseFont blodbf = null;
// BaseFont chineseFont = null;
// BaseFont chineseBlodFont = null;
try {
bf = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
blodbf = BaseFont.createFont(fontBlodPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
// chineseFont = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (IOException | DocumentException e) {
e.printStackTrace();
}
Font coverFont = new Font(blodbf, 30, Font.NORMAL);
Font titleFont = new Font(blodbf, 16, Font.NORMAL);
Font coverTiletFontMarked = new Font(blodbf, 16f, Font.NORMAL, new BaseColor(148, 170, 214));
Font textFontBold = new Font(blodbf, 10.5f, Font.NORMAL);
Font textFont = new Font(bf, 10.5f, Font.NORMAL);
Font textFontGray = new Font(blodbf, 10.5f, Font.NORMAL, new BaseColor(215, 215, 215));
Font chapterFont = new Font(blodbf, 14f, Font.NORMAL);
// Font coverFont = new Font(chineseFont, 30, Font.NORMAL);
// Font titleFont = new Font(chineseFont, 16, Font.NORMAL);
// Font coverTiletFontMarked = new Font(chineseFont, 16f, Font.NORMAL, new BaseColor(148, 170, 214));
// Font textFontBold = new Font(chineseFont, 10.5f, Font.NORMAL);
// Font textFont = new Font(chineseFont, 10.5f, Font.NORMAL);
// Font textFontGray = new Font(chineseFont, 10.5f, Font.NORMAL, new BaseColor(215, 215, 215));
// Font chapterFont = new Font(chineseFont, 14f, Font.NORMAL);
//封面
Paragraph cover = new Paragraph("\n\n\n"+"CompanyName In Here HAHAHAHAH"+"\nMy Company REPORT",coverFont);
cover.setAlignment(Element.ALIGN_CENTER);
Paragraph paragraph1 = new Paragraph("\n\n\nREPORT DATE: " +"2020/05/03 12:02:15", titleFont);
paragraph1.setAlignment(Element.ALIGN_RIGHT);
cover.add(paragraph1);
cover.add(new LineSeparator(5f, 100, new BaseColor(148, 170, 214), Element.ALIGN_CENTER, 30f));
cover.add(newLine);
Paragraph paragraph2 = new Paragraph("REPORT ID: " + "B124568744", coverTiletFontMarked);
paragraph2.setAlignment(Element.ALIGN_RIGHT);
cover.add(paragraph2);
document.add(cover);
document.newPage();
//添加首页
Paragraph home = new Paragraph(new Chunk("MY Report\n", chapterFont));
home.add(new Chunk("Report Type: Entity\n\n", textFontGray));
home.add(new Chunk("Company Name:\n", textFontBold));
home.add(new Chunk("JKLJI HJKHK NJNJ UHIU", titleFont));
document.add(home);
// document.add(line);
document.add(newLine);
document.add(newLine);
PdfPTable riskDashTb = new PdfPTable(2);
riskDashTb.setWidths(new float[]{0.4f, 0.6f});// 每个单元格占多宽
riskDashTb.setWidthPercentage(tableWidthPercentage);
riskDashTb.addCell(getCell(new Phrase("level", textFontBold), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("countryCode", textFont), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("Amount", textFontBold), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("nationality", textFont), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("Converted Credit Amount", textFontBold), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("addr", textFont), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("currency", textFontBold), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("busiZip", textFont), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("level", textFontBold), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("countryCode", textFont), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("Amount", textFontBold), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("nationality", textFont), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("Converted Credit Amount", textFontBold), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("addr", textFont), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("currency", textFontBold), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("busiZip", textFont), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("level", textFontBold), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("countryCode", textFont), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("Amount", textFontBold), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("nationality", textFont), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("Converted Credit Amount", textFontBold), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("addr", textFont), blue, 1, 1));
riskDashTb.addCell(getCell(new Phrase("currency", textFontBold), null, 1, 1));
riskDashTb.addCell(getCell(new Phrase("busiZip", textFont), null, 1, 1));
document.add(riskDashTb);
document.add(newLine);
document.add(new Paragraph("Note: The sdsd sdsd dsds is sd through a sdsds model that sdsd the sdsd of dsd of the sdsdsd company. aaa Amount is asas asa on sds saa dsdand other sdsd information.", textFont));
document.add(newLine);
Paragraph end = new Paragraph("End of the My Report", titleFont);
end.setAlignment(Element.ALIGN_CENTER);
document.add(end);
document.close();
// resp.getWriter().print("success");
}
private PdfPCell getCell(Phrase phrase, BaseColor color, int colSpan, int rowSpan) {
PdfPCell cells = new PdfPCell(phrase);
cells.setUseAscender(true);
cells.setMinimumHeight(20f);
cells.setHorizontalAlignment(Element.ALIGN_LEFT);
cells.setVerticalAlignment(5);
cells.setColspan(colSpan);
cells.setRowspan(rowSpan);
cells.setNoWrap(false);
if (color != null) {
cells.setBackgroundColor(color);
}
return cells;
}
}
接下来是页眉页脚事件的代码PDFBUilder
因为页眉页脚都是带格式的,所以需要一点一点拼接起来,页眉中需要添加logo图片,位置是通过代码中的x,y坐标指定的
package com.example.demo.utils;
import com.example.demo.config.PDFConfig;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* 设置页面附加属性
*
* @author hongli.zhang
* @create 2020/5/18 21:00
**/
@Component
public class PDFBuilder extends PdfPageEventHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(PDFBuilder.class);
public Phrase header;
public Phrase footer;
// 模板
public PdfTemplate total;
// 基础字体对象
public BaseFont bf = null;
// 利用基础字体生成的字体对象,一般用于生成中文文字
public Font fontDetail = null;
public PDFBuilder() {
}
public PDFBuilder(Phrase header, Phrase footer) {
this.header = header;
this.footer = footer;
}
public void setHeader(Phrase header) {
this.header = header;
}
public void setFooter(Phrase footer) {
this.footer = footer;
}
/**
* 文档打开时创建模板
*
* @param writer
* @param document
*/
public void onOpenDocument(PdfWriter writer, Document document) {
total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
}
/**
* 关闭每页的时候,写入页眉,页脚。
*
* @param writer
* @param document
*/
public void onEndPage(PdfWriter writer, Document document) {
this.addPage(writer, document);
//this.addWatermark(writer);
}
public static void setImg(PdfWriter writer, String path, float fitWidth, float fitHeight, float absoluteX, float absoluteY) {
try {
Image image = Image.getInstance(path);
image.setAlignment(Image.MIDDLE);
image.scaleToFit(fitWidth, fitHeight);
image.setAbsolutePosition(absoluteX, absoluteY);
writer.getDirectContent().addImage(image);
} catch (Exception e) {
LOGGER.error("[ERROR] Set Img : img file does not exist.");
}
}
//加分页
public void addPage(PdfWriter writer, Document document) {
PDFConfig config = InjectUtil.getInstance().getConfig();
if (document.getPageNumber() > 1) {
try {
bf = BaseFont.createFont(config.getCalibri(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
BaseFont blodf = null;
try {
blodf = BaseFont.createFont(config.getCalibrib(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
fontDetail = new Font(bf, 9, Font.NORMAL);// 数据体字体
Font titleFnot = new Font(blodf, 10.5f, Font.NORMAL);
Font blodFnot = new Font(blodf, 9f, Font.NORMAL);
Font font = new Font(bf, 10.5f, Font.NORMAL);
setImg(writer, "D:\\ceshi.jpg", 85, 45, 65, 785);
Phrase pageNumberPh = new Phrase(String.valueOf(document.getPageNumber() - 1), fontDetail);
float center = document.getPageSize().getRight() / 2;//页面的水平中点
float left = document.getPageSize().getLeft(90);//页面的z左边距
float headerleft = document.getPageSize().getLeft(180);//页面的z左边距
float right = document.getPageSize().getRight(90);//页面的z左边距
float top = document.getPageSize().getTop() - 36;
float bottom = document.getPageSize().getBottom() + 36;
header = new Paragraph("Beijing XXXX Information Technology Hn.,JKI.", titleFnot);
Paragraph header1 = new Paragraph("5f, building C2, hkhkjhd Industrial Park,", font);
Paragraph header2 = new Paragraph("No. 44, Changhu Road, Jiangxi Industrial Park", font);
Paragraph header3 = new Paragraph("www.xxx.com", font);
Paragraph header4 = new Paragraph("China (sss) xxxxxxxxxxxx Dgyhi Code Zone", font);
Paragraph header5 = new Paragraph("400-1234-567", font);
Paragraph footer1 = new Paragraph("Acnnecfds Kdjklsnf", blodFnot);
Paragraph footer2 = new Paragraph("The dfdfdf fddffdfdf by fdfdf in thfdfiscxcx fdfd is based on fdfd fdfd has been fdf fdfdf fdfdfdfd erwerwerwerwrww",
new Font(bf, 9, Font.NORMAL));
Paragraph footer3 = new Paragraph("ffdfdf and fdfdf fffdfd. fdsf fdfd fdfdf xcxcfdfd fdf fdf fdfd to fdfd that the dfdfdssxx is fdfdfdf and werwerwe",
new Font(bf, 9, Font.NORMAL));
Paragraph footer4 = new Paragraph("seewwdrr, we fdf fdfdf fdfd any fdfdfdxcx for fddfdfdfd, fdfdf, fdferere rererer or rer-rere rer and a werwerwer",
new Font(bf, 9, Font.NORMAL));
Paragraph footer5 = new Paragraph("rerer or rer of the rererer in rere rerer cxcxcxcxrer rere the retg of tfhe fffgvfgddsss on its sdfsd. The dddddw ",
new Font(bf, 9, Font.NORMAL));
Paragraph footer6 = new Paragraph("information dsfsdf is DSFSDS DFFDDD and Sdcxvxcvdfdfsd not be dfsdfsdf to any sdfsdf sdfsdfsd the dsfsdfsfdsdfee ",
new Font(bf, 9, Font.NORMAL));
Paragraph footer7 = new Paragraph("erdsfsdfsd. sdfsdf dsfsdf & dsfsdf dsfsdf sdfsdf of any sfddsfsdf dfdfdfdf the dfdf of the inforfmationssdeeetyy ",
new Font(bf, 9, Font.NORMAL));
// Paragraph footer8 = new Paragraph("dfdfddff in this report.", new Font(bf, 9, Font.NORMAL));
Paragraph footer9 = new Paragraph("[fdddfdfdfdf]", blodFnot);
// ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_CENTER, headerImg, center, top, 0); //页眉图标
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, header, headerleft, top + 13, 0); //页眉标题
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, header1, headerleft, top - 3, 0); //页眉内容
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, header2, headerleft, top - 15, 0); //页眉
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_RIGHT, header3, right, top - 15, 0); //页眉
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, header4, headerleft, top - 30, 0); //页眉
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_RIGHT, header5, right, top - 30, 0); //页眉
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_RIGHT, pageNumberPh, right, bottom + 65, 0); //页码
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_CENTER, footer1, center, bottom + 60, 0); //页脚标题
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer2, left, bottom + 50, 0); //页脚
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer3, left, bottom + 40, 0); //页脚
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer4, left, bottom + 30, 0); //页脚
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer5, left, bottom + 20, 0); //页脚
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer6, left, bottom + 10, 0); //页脚
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer7, left, bottom, 0); //页脚
// ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, footer8, left, bottom - 10, 0); //页脚
ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_CENTER, footer9, center, bottom - 20, 0); //页脚
}
}
//加水印
public void addWatermark(PdfWriter writer) {
// 水印图片
Image image;
try {
image = Image.getInstance("");
PdfContentByte content = writer.getDirectContentUnder();
content.beginText();
// 开始写入水印
for (int k = 0; k < 5; k++) {
for (int j = 0; j < 4; j++) {
image.setAbsolutePosition(150 * j, 170 * k);
content.addImage(image);
}
}
content.endText();
} catch (IOException | DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
*
*
* TODO 关闭文档时,替换模板,完成整个页眉页脚组件
* @see PdfPageEventHelper#onCloseDocument(PdfWriter,
* Document)
*/
public void onCloseDocument(PdfWriter writer, Document document) {
// 7.最后一步了,就是关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
total.beginText();
total.setFontAndSize(bf, 9);// 生成的模版的字体、颜色
String foot2 = " " + (writer.getPageNumber()) + " 页"; //页脚内容拼接 如 第1页/共2页
total.showText(foot2);// 模版显示的内容
total.endText();
total.closePath();
}
}
大概覆盖了大部分的功能,当然还有给字体加背景色,字体加颜色,和比较复杂的表格制作
比如需要将list中的数据按年份横排
我罗列出来可能需要用到的网站地址,