人生若只如初见

WXY

Java使用itext7生成PDF文件(三)

2023-12-31

本文深入讨论了Java中处理PDF的实用技巧,重点介绍了使用iText 7创建PDF文档的方法,以及运用Apache PDFBox将PDF文件转换成图片的技术,同时特别关注了中文字体显示问题的解决方案。

iText 7:生成 PDF

iText 7 是一个用于创建和操作 PDF 文档的开源 Java 库。它提供了丰富的功能来定制 PDF 的内容和格式。

先看下生成的pdf 和png最终效果

2023-12-29-ufaiyrhg.png

设置项目

  1. 在项目中添加 iText 7 依赖项。如果您使用 Maven,可以添加以下依赖到 pom.xml

  2. 这里我们使用当前最新版

     <!-- https://github.com/itext/itext7 -->
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext7-core</artifactId>
                <version>8.0.2</version>
                <type>pom</type>
            </dependency>

配置中文字体

1.把字体放到resources/fonts下

2023-12-29-tspicbzn.png2. 获取字体方法

  /**
     * 获取自定义字体
     *
     * @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

整体布局使用一个Table来布局,条码使绝对定位方式来确定位置

绝对定位时要注意:itextpdf的确定页面上的具体坐标是通过坐标来确定的。 它们的坐标轴是这样的:以左下角为原点的xy坐标轴。

2023-12-29-newkufzx.png

完整绘制方法代码

    /**
     * @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("072201130001EF");
        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("客运站结算单"));
//        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("无三品签字:  "));
        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("乘车站:  "));
        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("中心站"));
        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("车次: "));
        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("4038"));
        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("发车时间:  "));
        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("2023-12-11 16:00"));
        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("线路:  "));
        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("卫士"));
        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("公司:  "));
        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("xxx公司"));
        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("车辆:  "));
        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("豫A99999"));
        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("单据号:  "));
        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("E36666"));
        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("核定人数:  "));
        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("9999"));
        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("内外:  "));
        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("内结"));
        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("类别:  "));
        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("正常检票"));
        cell7_2_v.setTextAlignment(TextAlignment.LEFT);
        cell7_2_v.setBorder(Border.NO_BORDER);
        table.addCell(cell7_2_v);


        // 第8行
        List<String> titleList =
            Arrays.asList("到站", "票价", "全价", "优惠票", "售票站");
        for (int i = 0; i < 5; i++) {
            Cell cell =
                new Cell(1, 1).add(new Paragraph(titleList.get(i)));
            cell.setBorder(solidBorder);
            table.addCell(cell);
        }

        // 模拟数据列表
        ArrayList<Map> dataList = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            HashMap<String, String> map = new HashMap<>();
            map.put("到站", "某某");
            map.put("票价", "13");
            map.put("全价", "1");
            map.put("优惠票", "1");
            map.put("售票站", "34");
            dataList.add(map);
        }

        //   渲染数据
        dataList.forEach(item -> {
            titleList.forEach(title -> {
                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("总人数"));
        table.addCell(cell9_1);

        Cell cell9_2 = new Cell(1, 4).add(
            new Paragraph("20"));
        table.addCell(cell9_2);


        Cell cell10_1 = new Cell(1, 1).add(
            new Paragraph("结算金额"));
        table.addCell(cell10_1);

        Cell cell10_2 = new Cell(1, 4).add(
            new Paragraph("¥204.99"));
        table.addCell(cell10_2);


        Cell cell11_1 = new Cell(1, 1).add(
            new Paragraph(" "));
        table.addCell(cell11_1);

        Cell cell11_2 = new Cell(1, 4).add(
            new Paragraph("xxxxxx"));
        table.addCell(cell11_2);


        Cell cell12_1 = new Cell(1, 2).add(
            new Paragraph("检票员: "+"808-xxx"));
        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("打印时间: "+"2023-12-12 12:00:00"));
        cell12_2.setTextAlignment(TextAlignment.RIGHT);
        cell12_2.setBorder(Border.NO_BORDER);
        table.addCell(cell12_2);
        
        document.add(table);

    }

Apache PDFBox:PDF 转图片

Apache PDFBox 是另一个开源的 Java 库,用于处理 PDF 文件。它可以用于将 PDF 页面转换为图像。

设置项目

  1. 添加 Apache PDFBox 依赖项,我们这里也使用当前最新版:

            <!-- https://github.com/apache/pdfbox  -->
            <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>pdfbox</artifactId>
                <version>3.0.1</version>
            </dependency>

转换为图片

完整代码

 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 < pages; i++) {
                String imgFilePathPrefix = dstImgFolder + File.separator + imagePDFName;
                imgFilePath = new StringBuffer();
                imgFilePath.append(imgFilePathPrefix);
                imgFilePath.append(".png");
                File dstFile = new File(imgFilePath.toString());
                BufferedImage image = renderer.renderImageWithDPI(i, dpi);
                ImageIO.write(image, "png", dstFile);
            }
            System.out.println("PDF文档转PNG图片成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

如果pdf中文正常,转图片时图片中文乱码,可以把pdf字体策略修改为强制 EmbeddingStrategy.FORCE_EMBEDDED

运行测试


    public static void main(String[] args) throws IOException {

        //首先你需要一个writer对象
        PdfWriter writer = new PdfWriter("/home/wxy/Desktop/test.pdf");
        //需要一个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("/home/wxy/Desktop/test.pdf", "/home/wxy/Desktop/", 120);

    }

结语

结合使用 iText 7 和 Apache PDFBox,您可以轻松地在 Java 应用程序中生成和操作 PDF 文件。通过适当地处理中文字体,您可以确保 PDF 文档在不同环境下都能正确显示中文内容。

这只是一个简单的介绍。iText 7 和 Apache PDFBox 都提供了许多高级功能,可以用于更复杂的 PDF 处理任务。探索这些功能,您可以创建更加丰富和动态的文档。

相关文章

https://www.wxy97.com/archives/9

https://www.wxy97.com/archives/d4c10f84-e94c-4aa3-8da5-3b04618ba989