本文深入讨论了Java中处理PDF的实用技巧,重点介绍了使用iText 7创建PDF文档的方法,以及运用Apache PDFBox将PDF文件转换成图片的技术,同时特别关注了中文字体显示问题的解决方案。
iText 7:生成 PDF {#itext-7%EF%BC%9A%E7%94%9F%E6%88%90-pdf}
iText 7 是一个用于创建和操作 PDF 文档的开源 Java 库。它提供了丰富的功能来定制 PDF 的内容和格式。
先看下生成的pdf 和png最终效果
设置项目 {#%E8%AE%BE%E7%BD%AE%E9%A1%B9%E7%9B%AE}
-
在项目中添加 iText 7 依赖项。如果您使用 Maven,可以添加以下依赖到
pom.xml
: -
这里我们使用当前最新版
<!-- https://github.com/itext/itext7 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>8.0.2</version> <type>pom</type> </dependency>
配置中文字体 {#%E9%85%8D%E7%BD%AE%E4%B8%AD%E6%96%87%E5%AD%97%E4%BD%93}
1.把字体放到resources/fonts下
2. 获取字体方法
/**
* 获取自定义字体
*
* @return 自定义字体
*/
public static PdfFont getChineseFont() {
try {
// 字体文件的路径
String fontPath = "/home/wxy/Desktop/STSong-Light.ttf";
// PdfDemo02 为当前类
String fontPath2 = PdfDemo02.class.getResource("/fonts/classicSongSiJian.ttf").getPath();
// 使用文件路径创建字体 并指定嵌入策略
return PdfFontFactory.createFont(fontPath2, "Identity-H",
PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
} catch (IOException e) {
e.printStackTrace();
System.out.println("获取中文字体失败!");
return null;
}
}
字体下载: https://wangxy.lanzouw.com/b02rgxskh
EmbeddingStrategy 枚举是 iText 7 中 PdfFontFactory.createFont 方法的一部分,在这个枚举中,FORCE_EMBEDDED 和 PREFER_EMBEDDED 是最常用的选项,用于指定字体是否应该被嵌入到 PDF 文档中。
我们这里使用PREFER_EMBEDDED 嵌入字体,否则下文pdf生成图片时可能会中文乱码。
如果不考虑转图片,可以选择不嵌入字体,嵌入可能会稍微增大pdf文件。
绘制PDF {#%E7%BB%98%E5%88%B6pdf}
整体布局使用一个Table来布局,条码使绝对定位方式来确定位置
绝对定位时要注意:itextpdf的确定页面上的具体坐标是通过坐标来确定的。 它们的坐标轴是这样的:以左下角为原点的xy坐标轴。
完整绘制方法代码
/**
* @param document
*/
public static void drawBaseInfo(Document document) throws MalformedURLException {
// 黑色虚线边框
DeviceRgb black = new DeviceRgb(0, 0, 0);
SolidBorder solidBorder = new SolidBorder(black, 0.5F);
Table table = new Table(5);
table.useAllAvailableWidth();
table.setTextAlignment(TextAlignment.CENTER);
table.setFont(getChineseFont());
table.setFontSize(12);
table.setPadding(0);
// 条码
PdfDocument pdfDocument = document.getPdfDocument();
PdfPage page = pdfDocument.addNewPage();
Barcode128 barcode128 = new Barcode128(pdfDocument);
barcode128.setCode(&quot;072201130001EF&quot;);
barcode128.setCodeType(Barcode128.CODE128);
barcode128.fitWidth(180);
PdfFormXObject object =
barcode128.createFormXObject(ColorConstants.BLACK, ColorConstants.BLACK, pdfDocument);
float width = object.getWidth();
float height = object.getHeight();
float x = 200;
float y = 550;
PdfCanvas canvas = new PdfCanvas(page);
canvas.saveState();
canvas.setFillColor(ColorConstants.WHITE);
canvas.rectangle(x, y, width, height);
canvas.fill();
canvas.restoreState();
canvas.addXObjectAt(object, x, y);
// 顶部
Cell cell1 =
new Cell(1, 8).add(
new Paragraph(&quot;客运站结算单&quot;));
// cell1.setBold();
cell1.setFontSize(14);
cell1.setBorder(Border.NO_BORDER);
table.addCell(cell1);
table.setMarginTop(20);
Cell cell2_1 =
new Cell(1, 5).add(
new Paragraph(&quot;无三品签字: &quot;));
cell2_1.setTextAlignment(TextAlignment.RIGHT);
cell2_1.setBorder(Border.NO_BORDER);
cell2_1.setPaddingRight(80);
table.addCell(cell2_1);
Cell cell3_1 =
new Cell(1, 1).add(
new Paragraph(&quot;乘车站: &quot;));
cell3_1.setTextAlignment(TextAlignment.RIGHT);
cell3_1.setBorder(Border.NO_BORDER);
table.addCell(cell3_1);
Cell cell3_1_v =
new Cell(1, 2).add(
new Paragraph(&quot;中心站&quot;));
cell3_1_v.setTextAlignment(TextAlignment.LEFT);
cell3_1_v.setBorder(Border.NO_BORDER);
table.addCell(cell3_1_v);
Cell cell3_2 =
new Cell(1, 1).add(
new Paragraph(&quot;车次: &quot;));
cell3_2.setTextAlignment(TextAlignment.RIGHT);
cell3_2.setBorder(Border.NO_BORDER);
table.addCell(cell3_2);
Cell cell3_2_v =
new Cell(1, 1).add(
new Paragraph(&quot;4038&quot;));
cell3_2_v.setTextAlignment(TextAlignment.LEFT);
cell3_2_v.setBorder(Border.NO_BORDER);
table.addCell(cell3_2_v);
Cell cell4_1 =
new Cell(1, 1).add(
new Paragraph(&quot;发车时间: &quot;));
cell4_1.setTextAlignment(TextAlignment.RIGHT);
cell4_1.setBorder(Border.NO_BORDER);
table.addCell(cell4_1);
Cell cell4_1_v =
new Cell(1, 2).add(
new Paragraph(&quot;2023-12-11 16:00&quot;));
cell4_1_v.setTextAlignment(TextAlignment.LEFT);
cell4_1_v.setBorder(Border.NO_BORDER);
table.addCell(cell4_1_v);
Cell cell4_2 =
new Cell(1, 1).add(
new Paragraph(&quot;线路: &quot;));
cell4_2.setTextAlignment(TextAlignment.RIGHT);
cell4_2.setBorder(Border.NO_BORDER);
table.addCell(cell4_2);
Cell cell4_2_v =
new Cell(1, 1).add(
new Paragraph(&quot;卫士&quot;));
cell4_2_v.setTextAlignment(TextAlignment.LEFT);
cell4_2_v.setBorder(Border.NO_BORDER);
table.addCell(cell4_2_v);
Cell cell5_1 =
new Cell(1, 1).add(
new Paragraph(&quot;公司: &quot;));
cell5_1.setTextAlignment(TextAlignment.RIGHT);
cell5_1.setBorder(Border.NO_BORDER);
table.addCell(cell5_1);
Cell cell5_1_v =
new Cell(1, 2).add(
new Paragraph(&quot;xxx公司&quot;));
cell5_1_v.setTextAlignment(TextAlignment.LEFT);
cell5_1_v.setBorder(Border.NO_BORDER);
table.addCell(cell5_1_v);
Cell cell5_2 =
new Cell(1, 1).add(
new Paragraph(&quot;车辆: &quot;));
cell5_2.setTextAlignment(TextAlignment.RIGHT);
cell5_2.setBorder(Border.NO_BORDER);
table.addCell(cell5_2);
Cell cell5_2_v =
new Cell(1, 1).add(
new Paragraph(&quot;豫A99999&quot;));
cell5_2_v.setTextAlignment(TextAlignment.LEFT);
cell5_2_v.setBorder(Border.NO_BORDER);
table.addCell(cell5_2_v);
Cell cell6_1 =
new Cell(1, 1).add(
new Paragraph(&quot;单据号: &quot;));
cell6_1.setTextAlignment(TextAlignment.RIGHT);
cell6_1.setBorder(Border.NO_BORDER);
table.addCell(cell6_1);
Cell cell6_1_v =
new Cell(1, 2).add(
new Paragraph(&quot;E36666&quot;));
cell6_1_v.setTextAlignment(TextAlignment.LEFT);
cell6_1_v.setBorder(Border.NO_BORDER);
table.addCell(cell6_1_v);
Cell cell6_2 =
new Cell(1, 1).add(
new Paragraph(&quot;核定人数: &quot;));
cell6_2.setTextAlignment(TextAlignment.RIGHT);
cell6_2.setBorder(Border.NO_BORDER);
table.addCell(cell6_2);
Cell cell6_2_v =
new Cell(1, 1).add(
new Paragraph(&quot;9999&quot;));
cell6_2_v.setTextAlignment(TextAlignment.LEFT);
cell6_2_v.setBorder(Border.NO_BORDER);
table.addCell(cell6_2_v);
Cell cell7_1 =
new Cell(1, 1).add(
new Paragraph(&quot;内外: &quot;));
cell7_1.setTextAlignment(TextAlignment.RIGHT);
cell7_1.setBorder(Border.NO_BORDER);
table.addCell(cell7_1);
Cell cell7_1_v =
new Cell(1, 2).add(
new Paragraph(&quot;内结&quot;));
cell7_1_v.setTextAlignment(TextAlignment.LEFT);
cell7_1_v.setBorder(Border.NO_BORDER);
table.addCell(cell7_1_v);
Cell cell7_2 =
new Cell(1, 1).add(
new Paragraph(&quot;类别: &quot;));
cell7_2.setTextAlignment(TextAlignment.RIGHT);
cell7_2.setBorder(Border.NO_BORDER);
table.addCell(cell7_2);
Cell cell7_2_v =
new Cell(1, 1).add(
new Paragraph(&quot;正常检票&quot;));
cell7_2_v.setTextAlignment(TextAlignment.LEFT);
cell7_2_v.setBorder(Border.NO_BORDER);
table.addCell(cell7_2_v);
// 第8行
List&amp;lt;String&amp;gt; titleList =
Arrays.asList(&quot;到站&quot;, &quot;票价&quot;, &quot;全价&quot;, &quot;优惠票&quot;, &quot;售票站&quot;);
for (int i = 0; i &amp;lt; 5; i++) {
Cell cell =
new Cell(1, 1).add(new Paragraph(titleList.get(i)));
cell.setBorder(solidBorder);
table.addCell(cell);
}
// 模拟数据列表
ArrayList&amp;lt;Map&amp;gt; dataList = new ArrayList&amp;lt;&amp;gt;();
for (int i = 0; i &amp;lt; 6; i++) {
HashMap&amp;lt;String, String&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
map.put(&quot;到站&quot;, &quot;某某&quot;);
map.put(&quot;票价&quot;, &quot;13&quot;);
map.put(&quot;全价&quot;, &quot;1&quot;);
map.put(&quot;优惠票&quot;, &quot;1&quot;);
map.put(&quot;售票站&quot;, &quot;34&quot;);
dataList.add(map);
}
// 渲染数据
dataList.forEach(item -&amp;gt; {
titleList.forEach(title -&amp;gt; {
String value = String.valueOf(item.get(title));
Cell cell =
new Cell(1, 1).add(new Paragraph(value));
cell.setBorder(solidBorder).setFontColor(ColorConstants.RED);
table.addCell(cell);
});
});
Cell cell9_1 = new Cell(1, 1).add(
new Paragraph(&quot;总人数&quot;));
table.addCell(cell9_1);
Cell cell9_2 = new Cell(1, 4).add(
new Paragraph(&quot;20&quot;));
table.addCell(cell9_2);
Cell cell10_1 = new Cell(1, 1).add(
new Paragraph(&quot;结算金额&quot;));
table.addCell(cell10_1);
Cell cell10_2 = new Cell(1, 4).add(
new Paragraph(&quot;¥204.99&quot;));
table.addCell(cell10_2);
Cell cell11_1 = new Cell(1, 1).add(
new Paragraph(&quot; &quot;));
table.addCell(cell11_1);
Cell cell11_2 = new Cell(1, 4).add(
new Paragraph(&quot;xxxxxx&quot;));
table.addCell(cell11_2);
Cell cell12_1 = new Cell(1, 2).add(
new Paragraph(&quot;检票员: &quot;+&quot;808-xxx&quot;));
cell12_1.setBorder(Border.NO_BORDER);
cell12_1.setTextAlignment(TextAlignment.LEFT);
table.addCell(cell12_1);
Cell cell12_2 = new Cell(1, 3).add(
new Paragraph(&quot;打印时间: &quot;+&quot;2023-12-12 12:00:00&quot;));
cell12_2.setTextAlignment(TextAlignment.RIGHT);
cell12_2.setBorder(Border.NO_BORDER);
table.addCell(cell12_2);
document.add(table);
}</code></pre>
<br />
<br />
Apache PDFBox:PDF 转图片 {#apache-pdfbox%EF%BC%9Apdf-%E8%BD%AC%E5%9B%BE%E7%89%87}
Apache PDFBox 是另一个开源的 Java 库,用于处理 PDF 文件。它可以用于将 PDF 页面转换为图像。
设置项目 {#%E8%AE%BE%E7%BD%AE%E9%A1%B9%E7%9B%AE-1}
-
添加 Apache PDFBox 依赖项,我们这里也使用当前最新版:
<!-- https://github.com/apache/pdfbox -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>3.0.1</version>
</dependency>
转换为图片 {#%E8%BD%AC%E6%8D%A2%E4%B8%BA%E5%9B%BE%E7%89%87}
完整代码
public static void pdf2Image(String PdfFilePath, String dstImgFolder, int dpi) {
File file = new File(PdfFilePath);
try (PDDocument pdDocument = Loader.loadPDF(new File(PdfFilePath))) {
int dot = file.getName().lastIndexOf('.');
String imagePDFName = file.getName().substring(0, dot); // 获取图片文件名
PDFRenderer renderer = new PDFRenderer(pdDocument);
/* dpi越大转换后越清晰,相对转换速度越慢 */
int pages = 1;
StringBuffer imgFilePath = null;
for (int i = 0; i &amp;lt; pages; i++) {
String imgFilePathPrefix = dstImgFolder + File.separator + imagePDFName;
imgFilePath = new StringBuffer();
imgFilePath.append(imgFilePathPrefix);
imgFilePath.append(&quot;.png&quot;);
File dstFile = new File(imgFilePath.toString());
BufferedImage image = renderer.renderImageWithDPI(i, dpi);
ImageIO.write(image, &quot;png&quot;, dstFile);
}
System.out.println(&quot;PDF文档转PNG图片成功!&quot;);
} catch (IOException e) {
e.printStackTrace();
}
}&lt;/code&gt;&lt;/pre&gt;
<br />
如果pdf中文正常,转图片时图片中文乱码,可以把pdf字体策略修改为强制 EmbeddingStrategy.FORCE_EMBEDDED
<br />
运行测试 {#%E8%BF%90%E8%A1%8C%E6%B5%8B%E8%AF%95}
public static void main(String[] args) throws IOException {
//首先你需要一个writer对象
PdfWriter writer = new PdfWriter(&quot;/home/wxy/Desktop/test.pdf&quot;);
//需要一个pdf的对象来操作pdf
PdfDocument pdf = new PdfDocument(writer);
//需要一个document的对象来操作数据 指定为A4大小
try (Document document = new Document(pdf, PageSize.A5);) {
drawBaseInfo(document);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
pdf2Image(&quot;/home/wxy/Desktop/test.pdf&quot;, &quot;/home/wxy/Desktop/&quot;, 120);
}&lt;/code&gt;&lt;/pre&gt;
**结语** {#%E7%BB%93%E8%AF%AD}
----------------------------
结合使用 iText 7 和 Apache PDFBox,您可以轻松地在 Java 应用程序中生成和操作 PDF 文件。通过适当地处理中文字体,您可以确保 PDF 文档在不同环境下都能正确显示中文内容。
这只是一个简单的介绍。iText 7 和 Apache PDFBox 都提供了许多高级功能,可以用于更复杂的 PDF 处理任务。探索这些功能,您可以创建更加丰富和动态的文档。
&lt;br /&gt;
相关文章 {#%E7%9B%B8%E5%85%B3%E6%96%87%E7%AB%A0}
--------------------------------------------
https://51tbox.com/
https://51tbox.com/