前面讲解了如何使用 itextpdf7 给 PDF 添加水印,本次如何合并多个 PDF 文件为一个,在 itextpdf7 中代码非常简单

  1. // ~ pdf ==========================
  2. implementation 'com.itextpdf:itext7-core:7.1.16'
  3. implementation 'cn.hutool:hutool-all:5.5.4'

合并 PDF 文件

  1. package cn.mrcode;
  2. import com.itextpdf.kernel.pdf.PdfDocument;
  3. import com.itextpdf.kernel.pdf.PdfReader;
  4. import com.itextpdf.kernel.pdf.PdfWriter;
  5. import com.itextpdf.kernel.utils.PdfMerger;
  6. import java.io.FileNotFoundException;
  7. import java.io.IOException;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. /**
  11. * @author mrcode
  12. * @date 2022/1/21 20:28
  13. */
  14. public class Pdf7Demo {
  15. public static void main(String[] args) throws IOException {
  16. final List<String> list = new ArrayList<>();
  17. list.add("C:/pdf/1.pdf");
  18. list.add("C:/pdf/2.pdf");
  19. final String toFile = "C:/pdf/result.pdf";
  20. mergePdfFiles(list, toFile);
  21. }
  22. /**
  23. * 合并多个 PDF 文件为一个
  24. * @param list 要合并的 PDF 文件绝对路径列表
  25. * @param toFile 合并后的 PDF 文件绝对路径
  26. * @throws IOException
  27. */
  28. public static void mergePdfFiles(List<String> list, String toFile) throws IOException {
  29. final PdfDocument toPdf = new PdfDocument(new PdfWriter(toFile));
  30. PdfMerger merger = new PdfMerger(toPdf);
  31. for (String file : list) {
  32. final PdfDocument pdfDocument = new PdfDocument(new PdfReader(file));
  33. merger.merge(pdfDocument, 1, pdfDocument.getNumberOfPages());
  34. pdfDocument.close();
  35. }
  36. merger.close();
  37. toPdf.close();
  38. }
  39. }

给 pdf 添加页码

前面合并代码很简单,但是添加页码就要稍微复杂点了,其实在前面添加水印的时候已经使用过这个功能了,添加页面无非就是需要自己计算一些信息:

  1. 当前页
  2. 总页数
  3. 生成页码文案,比如 1/100
  4. 将文案添加到 PDF 指定位置

从这个流程来看,前面添加水印的时候其实已经做过了第 4 个步骤。所以这就简单了,自己使用水印的代码改一改就可以了

  1. package cn.mrcode.pdf;
  2. import com.itextpdf.io.font.PdfEncodings;
  3. import com.itextpdf.io.font.constants.StandardFonts;
  4. import com.itextpdf.kernel.font.PdfFont;
  5. import com.itextpdf.kernel.font.PdfFontFactory;
  6. import com.itextpdf.kernel.geom.Rectangle;
  7. import com.itextpdf.kernel.pdf.PdfDocument;
  8. import com.itextpdf.kernel.pdf.PdfPage;
  9. import com.itextpdf.kernel.pdf.PdfReader;
  10. import com.itextpdf.kernel.pdf.PdfWriter;
  11. import com.itextpdf.layout.Document;
  12. import com.itextpdf.layout.element.Paragraph;
  13. import com.itextpdf.layout.property.TextAlignment;
  14. import com.itextpdf.layout.property.VerticalAlignment;
  15. import java.io.IOException;
  16. import cn.hutool.core.util.StrUtil;
  17. /**
  18. * @author mrcode
  19. * @date 2022/1/21 20:27
  20. */
  21. public class PDFUtil {
  22. /**
  23. * 添加页码; 在右下角添加
  24. *
  25. * @param fontSize 文字大小,一般为 15 比较合适
  26. * @param srcPath 原始 PDF 文件绝对路径
  27. * @param destPath 添加完水印后的 PDF 存放路径
  28. */
  29. public static void addPageNumber(int fontSize, String srcPath, String destPath) throws IOException {
  30. PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPath), new PdfWriter(destPath));
  31. Document doc = new Document(pdfDoc);
  32. PdfFont font = getPdfFont(null);
  33. // 文字高度则是字体大小
  34. final float textHeight = fontSize;
  35. final int numberOfPages = pdfDoc.getNumberOfPages();
  36. for (int i = 1; i <= numberOfPages; i++) {
  37. PdfPage pdfPage = pdfDoc.getPage(i);
  38. // 获取页面大小,考虑页面旋转
  39. Rectangle pageSize = pdfPage.getPageSizeWithRotation();
  40. // 当页面有旋转时,内容自动旋转
  41. pdfPage.setIgnorePageRotationForContent(true);
  42. // 构建页码
  43. final String text = StrUtil.format("{}/{}", i, numberOfPages);
  44. Paragraph paragraph = new Paragraph(text)
  45. .setFont(font)
  46. .setFontSize(fontSize);
  47. // 获取文字宽度
  48. final float textWidth = font.getWidth(text, fontSize);
  49. // 计算添加的位置坐标
  50. // 定位到水平垂直居中
  51. // float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
  52. // 定位到右侧:根据文字宽度减少宽度,能动态的根据文字宽度调整,让文字不会超出屏幕外面
  53. float x = pageSize.getRight() - textWidth;
  54. // bottom 是 0,+ 20 就是底部往上 20
  55. // 定位到底部:根据文字高度动态往上调整,不会超出屏幕外面;
  56. float y = pageSize.getBottom() + textHeight + 10;
  57. // 参数分别为:文本、x 坐标、y 坐标、添加到底几页、文本水平对齐方式、文本垂直对齐方式、旋转弧度
  58. doc.showTextAligned(paragraph,
  59. x, // 文本所在 x y 坐标,文字将围绕这个点进行对齐或则旋转
  60. y,
  61. i, // 添加到 PDF 第几页
  62. TextAlignment.CENTER, // 文本水平对齐方式
  63. VerticalAlignment.TOP, // 文本垂直对齐方式
  64. // 将 角度 转换为 弧度
  65. (float) Math.toRadians(0));
  66. }
  67. doc.close();
  68. }
  69. /**
  70. * 获取字体,支持 afm、pfm、ttf、otf、woff、woff2 字体,ttc 目前直接报错
  71. *
  72. * @param fontPath 字体文件绝对路径,如果为空则返回默认的英文字体
  73. * @return
  74. */
  75. private static PdfFont getPdfFont(String fontPath) throws IOException {
  76. if (fontPath == null) {
  77. return PdfFontFactory.createFont(StandardFonts.HELVETICA);
  78. }
  79. return PdfFontFactory.createFont(
  80. fontPath,
  81. // 水平书写
  82. PdfEncodings.IDENTITY_H,
  83. // 是否将字体嵌入到目标文档中: 如果可能,嵌入字体
  84. PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
  85. }
  86. }

生成效果
image.png

最终工具类

  1. package cn.mrcode.pdf;
  2. import com.itextpdf.io.font.PdfEncodings;
  3. import com.itextpdf.io.font.constants.StandardFonts;
  4. import com.itextpdf.kernel.font.PdfFont;
  5. import com.itextpdf.kernel.font.PdfFontFactory;
  6. import com.itextpdf.kernel.geom.Rectangle;
  7. import com.itextpdf.kernel.pdf.PdfDocument;
  8. import com.itextpdf.kernel.pdf.PdfPage;
  9. import com.itextpdf.kernel.pdf.PdfReader;
  10. import com.itextpdf.kernel.pdf.PdfWriter;
  11. import com.itextpdf.kernel.utils.PdfMerger;
  12. import com.itextpdf.layout.Document;
  13. import com.itextpdf.layout.element.Paragraph;
  14. import com.itextpdf.layout.property.TextAlignment;
  15. import com.itextpdf.layout.property.VerticalAlignment;
  16. import java.io.IOException;
  17. import java.util.List;
  18. import cn.hutool.core.util.StrUtil;
  19. /**
  20. * @author mrcode
  21. * @date 2022/1/21 20:27
  22. */
  23. public class PDFUtil {
  24. /**
  25. * 合并多个 PDF 文件为一个
  26. *
  27. * @param list 要合并的 PDF 文件绝对路径列表
  28. * @param toFile 合并后的 PDF 文件绝对路径
  29. * @throws IOException
  30. */
  31. public static void merge(List<String> list, String toFile) throws IOException {
  32. final PdfDocument toPdf = new PdfDocument(new PdfWriter(toFile));
  33. PdfMerger merger = new PdfMerger(toPdf);
  34. for (String file : list) {
  35. final PdfDocument pdfDocument = new PdfDocument(new PdfReader(file));
  36. merger.merge(pdfDocument, 1, pdfDocument.getNumberOfPages());
  37. pdfDocument.close();
  38. }
  39. merger.close();
  40. toPdf.close();
  41. }
  42. /**
  43. * 添加页码; 在右下角添加
  44. *
  45. * @param fontSize 文字大小,一般为 15 比较合适
  46. * @param srcPath 原始 PDF 文件绝对路径
  47. * @param destPath 添加完水印后的 PDF 存放路径
  48. */
  49. public static void addPageNumber(int fontSize, String srcPath, String destPath) throws IOException {
  50. PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPath), new PdfWriter(destPath));
  51. Document doc = new Document(pdfDoc);
  52. PdfFont font = getPdfFont(null);
  53. // 文字高度则是字体大小
  54. final float textHeight = fontSize;
  55. final int numberOfPages = pdfDoc.getNumberOfPages();
  56. for (int i = 1; i <= numberOfPages; i++) {
  57. PdfPage pdfPage = pdfDoc.getPage(i);
  58. // 获取页面大小,考虑页面旋转
  59. Rectangle pageSize = pdfPage.getPageSizeWithRotation();
  60. // 当页面有旋转时,内容自动旋转
  61. pdfPage.setIgnorePageRotationForContent(true);
  62. // 构建页码
  63. final String text = StrUtil.format("{}/{}", i, numberOfPages);
  64. Paragraph paragraph = new Paragraph(text)
  65. .setFont(font)
  66. .setFontSize(fontSize);
  67. // 获取文字宽度
  68. final float textWidth = font.getWidth(text, fontSize);
  69. // 计算添加的位置坐标
  70. // 定位到水平垂直居中
  71. // float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
  72. // 定位到右侧:根据文字宽度减少宽度,能动态的根据文字宽度调整,让文字不会超出屏幕外面
  73. float x = pageSize.getRight() - textWidth;
  74. // bottom 是 0,+ 20 就是底部往上 20
  75. // 定位到底部:根据文字高度动态往上调整,不会超出屏幕外面;
  76. float y = pageSize.getBottom() + textHeight + 10;
  77. // 参数分别为:文本、x 坐标、y 坐标、添加到底几页、文本水平对齐方式、文本垂直对齐方式、旋转弧度
  78. doc.showTextAligned(paragraph,
  79. x, // 文本所在 x y 坐标,文字将围绕这个点进行对齐或则旋转
  80. y,
  81. i, // 添加到 PDF 第几页
  82. TextAlignment.CENTER, // 文本水平对齐方式
  83. VerticalAlignment.TOP, // 文本垂直对齐方式
  84. // 将 角度 转换为 弧度
  85. (float) Math.toRadians(0));
  86. }
  87. doc.close();
  88. }
  89. /**
  90. * 获取字体,支持 afm、pfm、ttf、otf、woff、woff2 字体,ttc 目前直接报错
  91. *
  92. * @param fontPath 字体文件绝对路径,如果为空则返回默认的英文字体
  93. * @return
  94. */
  95. private static PdfFont getPdfFont(String fontPath) throws IOException {
  96. if (fontPath == null) {
  97. return PdfFontFactory.createFont(StandardFonts.HELVETICA);
  98. }
  99. return PdfFontFactory.createFont(
  100. fontPath,
  101. // 水平书写
  102. PdfEncodings.IDENTITY_H,
  103. // 是否将字体嵌入到目标文档中: 如果可能,嵌入字体
  104. PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
  105. }
  106. }

如果自己想定制 字体颜色和文案位置,可以仔细阅读下上面源码注释,还有前面添加水印的文章中有字体颜色的注释