<dependency>
<groupId>com.itextpdfgroupId>
<artifactId>itextpdfartifactId>
<version>5.5.10version>
dependency>
<dependency>
<groupId>com.itextpdfgroupId>
<artifactId>itext-asianartifactId>
<version>5.2.0version>
dependency>
通过PdfPageEventHelper事件可以动态的创建页眉,数据构建出多少页pdf就有多少页页眉
package com.example.pdf;
import com.example.pdf.vo.RenovationDocNameEnum;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
/**
* 内部类 添加页眉、页脚
*/
public class PdfEvent extends PdfPageEventHelper {
// 一页加载完成触发,写入页眉和页脚
@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfPTable head = new PdfPTable(1);
PdfObject businessType = document.getAccessibleAttribute(new PdfName("businessType"));
PdfObject flag = document.getAccessibleAttribute(new PdfName("flag"));
try {
if (RenovationDocNameEnum.CB.getName().equals(businessType.toString())
|| (RenovationDocNameEnum.SG.getName().equals(businessType.toString()) && flag != null && "1".equals(flag.toString()))) {
setCbPageHead(head, writer, document);
} else if (RenovationDocNameEnum.SG.getName().equals(businessType.toString()) || RenovationDocNameEnum.JC.getName().equals(businessType.toString())) {
setSinglePageHead(head, writer, document);
}
} catch (Exception e) {
throw new ExceptionConverter(e);
}
}
/**
* Chunk的offsetX控制页眉图片水平位置,offsetY会影响到图片大小; 图片上下位置可通过showTextAligned的y来控制
* 页眉下划线通过setTotalWidth控制先长度,writeSelectedRows的xPos控制线的起始水平位置
*
* @param head
* @param writer
* @param document
* @throws DocumentException
*/
private void setSinglePageHead(PdfPTable head, PdfWriter writer, Document document) throws DocumentException {
Font font = PdfUtil.getFont(null);
// 通过表格构建页眉下划线
head.setTotalWidth(PageSize.A4.getWidth() - 105);
head.setWidths(new int[]{24});
head.setLockedWidth(true);
head.getDefaultCell().setFixedHeight(-10);
head.getDefaultCell().setBorder(Rectangle.BOTTOM);
head.getDefaultCell().setBorderWidth(0.5f);
head.addCell(new Paragraph(" ", font));
// 将页眉写到document中,位置可以指定,指定到下面就是页脚
head.writeSelectedRows(0, -1, 55, PageSize.A4.getHeight() - 20, writer.getDirectContent());
PdfContentByte directContent = writer.getDirectContent();
// 最重要的是这个,如果页眉需要设置图片的话,需要在Phrase对象中添加一个Chunk对象,在Chunk对象中添加图片信息即可
Phrase phrase = new Phrase("", font);
Image img = PdfUtil.getImg();
if (img != null) {
phrase.add(new Chunk(img, 30, -150));
}
// 写入页眉
ColumnText.showTextAligned(directContent, Element.ALIGN_RIGHT, phrase, document.right(), PageSize.A4.getHeight() + 48, 0);
}
- 通过PdfPTable来构建下划线
- 通过new Chunk来显示图片,并通过ColumnText.showTextAligned来展示在页眉
- 详细见setSinglePageHead方法的注释,方正就是一顿调,知道合适位置
- 像下述这样定义一个字符串参数于document,就可以像上述PdfEvent中获取做业务判断
- 使用的时候注意toString()
PdfName businessType = new PdfName("businessType");
document.setAccessibleAttribute(businessType, new PdfString(type));
- 主要以下两个方法,FileUtil是hutool的工具类,Base64是java.util的工具类
PdfUtil.getBase64(FileUtil.readBytes(file));
public static String getBase64(byte[] buffer) {
return Base64.getEncoder().encodeToString(buffer);
}
本人生成pdf时用的是simkai.ttf字体,输出的pdf没有显示²上标
- 要么可以换字体,百度即可
- 要么像如下这样,通过setTextRise来控制文字位置,来大概展示成²效果
- 方法正值为上标,负值可为下标,注意上标的2的文字specialFont字体要定义小点,本人定义5
public static PdfPCell getPDFCellSpecial(String name, Font font, Integer alignment) {
PdfPCell cell = new PdfPCell();
if (name == null) {
name = " ";
}
Paragraph p = new Paragraph(name, font);
Font specialFont = PdfUtil.getSpecialFont(null);
Chunk chunk = new Chunk("2", specialFont);
chunk.setTextRise(5f);
p.add(chunk);
p.add(new Chunk(")", font));
if (alignment == null) {
p.setAlignment(Element.ALIGN_CENTER);
} else {
p.setAlignment(alignment);
}
cell.setUseAscender(true);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.addElement(p);
return cell;
}
- 业务上从别的接口获取到一个zip包的文件流,需要将zip包的文件输出到目录,如下代码:
- 涉及IO流要注意关流
private static List<String> extractAllByInputStream(InputStream inputStream, String pdfPath) {
ArrayList<String> list = Lists.newArrayList();
try {
dealZipInputStream(inputStream, pdfPath, list);
} catch (IOException e) {
LOGGER.info("下载pdf到本地异常,异常信息:{}", e);
}
return list;
}
private static void dealZipInputStream(InputStream inputStream, String pdfPath, ArrayList<String> list) throws IOException {
byte[] buffer = new byte[1024];
ZipEntry zipEntry;
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String entryName = zipEntry.getName();
if (!zipEntry.isDirectory()) {
String fileName;
if (entryName.contains(RenovationDocNameEnum.CB.getName())) {
fileName = pdfPath + RenovationDocNameEnum.CB.getName() + ".pdf";
} else if (entryName.contains(RenovationDocNameEnum.JC.getName())) {
fileName = pdfPath + RenovationDocNameEnum.JC.getName() + ".pdf";
} else if (entryName.contains(RenovationDocNameEnum.SG.getName())) {
fileName = pdfPath + RenovationDocNameEnum.SG.getName() + ".pdf";
} else {
fileName = pdfPath + UUID.randomUUID() + ".pdf";
}
list.add(fileName);
File subFile = new File(fileName);
subFile.createNewFile();
readSubFile(subFile, zipInputStream, buffer);
}
zipInputStream.closeEntry();
}
} finally {
IOUtils.closeQuietly(inputStream);
}
}
private static void readSubFile(File subFile, ZipInputStream zipInputStream, byte[] buffer) {
int len;
try (FileOutputStream fileOut = new FileOutputStream(subFile)) {
while ((len = zipInputStream.read(buffer)) > 0) {
fileOut.write(buffer, 0, len);
}
} catch (IOException e) {
throw new ExceptionConverter(e);
}
}
- 本人文件合并后要删除之前的文件,这时需要注意new PdfReader(inputStream)时使用inputStream
- 不然可能出现:别的程序正在使用而无法删除情况
/* 合并pdf文件
* @param files 要合并文件数组(绝对路劲{ "D:\\a.pdf", "D:\\b.pdf" })
* @param savePath 合并后新产生的文件绝对路径如D:\\temp.pdf
*/
public static Integer mergePdfFiles(String[] files, String savePath) {
Document document = null;
PdfCopy copy = null;
try (FileOutputStream outputStream = new FileOutputStream(savePath)) {
// 创建一个与a.pdf相同纸张大小的document
document = new Document(new PdfReader(files[0]).getPageSize(1));
copy = new PdfCopy(document, outputStream);
document.open();
for (int i = 0; i < files.length; i++) {
// 一个一个的遍历现有的PDF
dealFile(files[i], copy, document);
}
copy.close();
document.close();
return 1;
} catch (IOException | DocumentException e) {
LOGGER.info("合并pdf失败,异常信息:{}", e);
} finally {
if (document != null) {
document.close();
}
if (copy != null) {
copy.close();
}
}
return 0;
}
private static void dealFile(String file, PdfCopy copy, Document document) {
PdfReader reader = null;
try (FileInputStream inputStream = new FileInputStream(file)) {
reader = new PdfReader(inputStream);
int n = reader.getNumberOfPages();// PDF文件总共页数
for (int j = 1; j <= n; j++) {
document.newPage();
PdfImportedPage page = copy.getImportedPage(reader, j);
copy.addPage(page);
}
reader.close();
} catch (IOException | BadPdfFormatException e) {
throw new ExceptionConverter(e);
} finally {
if (reader != null) {
reader.close();
}
}
}