Skip to content

Java使用iText 7将HTML转PDF示例详解

Published: at 17:38:28

一、简介

在Java开发中,有时我们需要将HTML内容转换为PDF文件。iText 7是这方面的一个不错选择,它功能强大且灵活。这篇文章我会分享如何使用iText 7来实现HTML转PDF的过程,包括一些关键概念和实现细节。

二、核心组件

使用iText 7转换HTML到PDF,主要涉及这几个核心组件:

  1. ConverterProperties:用于配置转换过程中的各种参数
  2. FontProvider:负责处理字体渲染,特别是对中文等非ASCII字符的支持
  3. PdfWriter:将内容写入到输出流
  4. PdfDocument:代表整个PDF文档的结构
  5. HtmlConverter:真正执行HTML到PDF转换的类

三、转换流程详解

0. 引入依赖

首先需要在pom.xml中添加相关依赖:

<!-- iText 7 Core -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>kernel</artifactId>
    <version>7.2.5</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>layout</artifactId>
    <version>7.2.5</version>
</dependency>

<!-- HTML 转换模块 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>4.0.4</version>
</dependency>

1. 创建转换配置

我们需要创建并配置ConverterProperties对象,其中最关键的是配置字体:

private static ConverterProperties createConverterProperties() {
    ConverterProperties properties = new ConverterProperties();
    FontProvider fontProvider = new FontProvider();
    // 添加系统所有字体,支持中文等多语言
    fontProvider.addSystemFonts();
    properties.setFontProvider(fontProvider);
    return properties;
}

这一步很重要,尤其是处理中文内容时。通过addSystemFonts()方法,我们可以加载系统中所有可用字体,确保PDF能正确显示各种语言。

2. 准备PDF输出

接下来创建PdfWriter和PdfDocument对象:

PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);

这里的outputStream可以是文件流、内存流或其他任何输出流。

3. 执行转换

有了前面的准备工作,现在可以用HtmlConverter来进行实际的转换了:

// 从HTML字符串转换
HtmlConverter.convertToPdf(htmlContent, pdfDocument, properties);

// 或从HTML文件转换
HtmlConverter.convertToPdf(Files.newInputStream(htmlFile.toPath()), pdfDocument, properties);

4. 资源释放

别忘了在完成后释放资源:

// 使用try-with-resources自动处理资源释放
try (OutputStream outputStream = new FileOutputStream("output.pdf")) {
    // 转换过程
}

四、高级功能与配置

1. 字体处理

字体处理是HTML转PDF中最头疼的问题之一,特别是对中文支持。除了前面提到的addSystemFonts(),我们还可以:

// 添加特定字体
fontProvider.addFont("/path/to/font.ttf");

// 指定默认字体
fontProvider.addFont("SimSun"); // 宋体通常对中文支持较好

2. 图片处理

处理图片时,需要正确设置基础URI:

// 设置基础URI,用于解析HTML中相对路径的图片
properties.setBaseUri("file:///path/to/images/");

3. CSS支持

iText的HTML转PDF支持大部分CSS特性:

// 添加外部CSS
properties.setCssAppliers(new DefaultCssApplierFactory());
// 添加自定义CSS文件
properties.addCssFile("/path/to/style.css");

五、常见问题及解决方案

在实际使用过程中,我遇到过一些常见问题,这里分享解决方案:

1. 中文显示问题

如果PDF中的中文显示为方块或问号,通常是字体问题:

// 确保添加了支持中文的字体
fontProvider.addFont("SimSun"); // 使用宋体
// 或者
fontProvider.addSystemFonts(); // 添加所有系统字体

2. 布局问题

HTML到PDF的转换可能存在布局差异,特别是复杂布局:

// 设置文档大小
pdfDocument.setDefaultPageSize(PageSize.A4);

// 设置页边距(上右下左)
properties.setMargins(new float[]{36, 36, 36, 36});

3. 图片路径问题

图片无法显示通常是因为路径解析问题:

// 设置正确的基础URI
properties.setBaseUri("file:///path/to/html/directory/");

六、性能优化

在处理大量文档或大型HTML文件时,性能优化很重要:

1. 内存管理

处理大型HTML文件时:

// 设置处理大型HTML的缓冲区大小
properties.setBufferSize(8192);

// 分页处理大型文档
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
pdfDocument.setTagged();
pdfDocument.setDefaultPageSize(PageSize.A4);

2. 并发处理

对于批量处理场景,可以使用线程池:

// 使用线程池并发处理多个文档
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<?>> futures = new ArrayList<>();

for(String html : htmlList) {
    futures.add(executor.submit(() -> {
        convertHtmlToPdf(html, new FileOutputStream("output_" + count.getAndIncrement() + ".pdf"));
        return null;
    }));
}

七、实际应用示例

下面是一些我在实际项目中使用的示例:

1. 基本转换

public static void simpleConversion(String html, String outputPath) {
    try (FileOutputStream fos = new FileOutputStream(outputPath)) {
        PdfWriter writer = new PdfWriter(fos);
        PdfDocument pdf = new PdfDocument(writer);
        ConverterProperties properties = createConverterProperties();
        HtmlConverter.convertToPdf(html, pdf, properties);
    } catch (IOException e) {
        log.error("转换失败", e);
    }
}

2. 生成并返回Base64编码

这在Web应用中特别有用,可以直接将PDF作为Base64字符串返回给前端:

public static String generatePdfBase64(String html) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        PdfWriter writer = new PdfWriter(baos);
        PdfDocument pdf = new PdfDocument(writer);
        ConverterProperties properties = createConverterProperties();
        HtmlConverter.convertToPdf(html, pdf, properties);
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    } catch (IOException e) {
        log.error("生成PDF Base64失败", e);
        return null;
    }
}

3. 处理模板文件

结合模板引擎使用非常方便:

public static void processTemplate(Map<String, Object> data, String templatePath, String outputPath) {
    // 先使用模板引擎处理HTML模板
    String processedHtml = processTemplate(templatePath, data); // 使用Thymeleaf或其他模板引擎
    // 再转换为PDF
    convertHtmlToPdf(processedHtml, new FileOutputStream(outputPath));
}

4. 完整的工具类

这是我整合的一个完整工具类,可以直接拿去用:

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.Files;
import java.util.Base64;

/**
 * HTML转PDF工具类
 */
@Slf4j
public class HtmlToPdfUtil {

    /**
     * 创建转换属性
     */
    private static ConverterProperties createConverterProperties() {
        ConverterProperties properties = new ConverterProperties();
        FontProvider fontProvider = new FontProvider();
        // 添加中文字体支持
        fontProvider.addSystemFonts();
        properties.setFontProvider(fontProvider);
        return properties;
    }

    /**
     * 将HTML内容转换为PDF
     */
    private static void convertToPdf(String htmlContent, OutputStream outputStream) {
        try {
            // 创建PDF文档
            PdfWriter pdfWriter = new PdfWriter(outputStream);
            PdfDocument pdfDocument = new PdfDocument(pdfWriter);

            // 转换HTML到PDF
            HtmlConverter.convertToPdf(htmlContent, pdfDocument, createConverterProperties());
        } catch (Exception e) {
            log.error("HTML转PDF失败", e);
            throw new RuntimeException("HTML转PDF失败", e);
        }
    }

    /**
     * 将HTML文件转换为PDF
     */
    private static void convertFileToPdf(File htmlFile, OutputStream outputStream) {
        try {
            // 创建PDF文档
            PdfWriter pdfWriter = new PdfWriter(outputStream);
            PdfDocument pdfDocument = new PdfDocument(pdfWriter);

            // 转换HTML文件到PDF
            HtmlConverter.convertToPdf(Files.newInputStream(htmlFile.toPath()),
                                      pdfDocument, createConverterProperties());
        } catch (Exception e) {
            log.error("HTML文件转PDF失败", e);
            throw new RuntimeException("HTML文件转PDF失败", e);
        }
    }

    // 公开API方法
    public static void convertHtmlToPdf(String htmlContent, OutputStream outputStream) {
        convertToPdf(htmlContent, outputStream);
    }

    public static void convertHtmlFileToPdf(File htmlFile, OutputStream outputStream) {
        convertFileToPdf(htmlFile, outputStream);
    }

    public static void convertHtmlToPdfFile(String htmlContent, File outputFile) {
        try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
            convertToPdf(htmlContent, outputStream);
        } catch (Exception e) {
            log.error("HTML转PDF文件失败", e);
            throw new RuntimeException("HTML转PDF文件失败", e);
        }
    }

    public static String convertHtmlToPdfBase64(String htmlContent) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            convertToPdf(htmlContent, outputStream);
            return Base64.getEncoder().encodeToString(outputStream.toByteArray());
        } catch (Exception e) {
            log.error("HTML转PDF Base64失败", e);
            throw new RuntimeException("HTML转PDF Base64失败", e);
        }
    }

    public static String convertHtmlFileToPdfBase64(File htmlFile) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            convertFileToPdf(htmlFile, outputStream);
            return Base64.getEncoder().encodeToString(outputStream.toByteArray());
        } catch (Exception e) {
            log.error("HTML文件转PDF Base64失败", e);
            throw new RuntimeException("HTML文件转PDF Base64失败", e);
        }
    }
}

八、对比其他解决方案

在选择HTML转PDF方案时,我对比了几种主流技术:

  1. iText 7: 功能全面,性能很好,支持复杂格式,但商业使用需要授权费
  2. PDFBox: Apache开源项目,完全免费,但HTML渲染能力比较弱
  3. wkhtmltopdf: 基于WebKit的命令行工具,渲染质量高,但需要系统安装
  4. Flying Saucer: 开源纯Java实现,通常结合iText使用,不支持现代CSS特性

根据实际需求和场景,iText 7在大多数情况下是一个不错的选择,特别是对于复杂的HTML内容转换需求。

九、总结

通过iText 7实现HTML到PDF的转换并不复杂,关键是正确配置ConverterProperties和FontProvider。在实际项目中,最容易遇到的问题是字体和布局问题,只要掌握了本文介绍的技巧,基本都能解决。

如果你有复杂的报表或文档需求,使用iText 7配合HTML模板是一个高效且灵活的方案。希望这篇文章对你有所帮助!