itext的pdf2html项目实践

itext
placeholder image
admin 发布于:2022-07-10 18:37:14
阅读:loading

1.基本介绍

本站功能在逐步实现后,开始完善站内的博客文章,在文章预览页增加了将当前文章下载为PDF文件(限制了用户必须登录)的功能实现,所以当对PDF的实现组件略有梳理后最终使用了PDF的itext中的html2pdf子项目,该项目可以将HTML转换为PDF,只需要导入依赖的maven坐标即可,无需依赖其它第三方服务插件等。

pdfHtml是itext7的附加组件,官网描述为pdfHtml,本人在本文中所声明的html2pdf想表达的是与pdfHtml是同一个意思,只是html2pdf更符合实际,将html代码转换为pdf文档,主要这个命名还是以官方在GitHub上提供的pdfHtml项目的示例源码定义的名称一致,参考项目示例源码地址为:https://github.com/itext/i7j-pdfhtml,导入IDEA后的工程名称为html2pdf。

在此前也将该项目下载下来后,逐个的运行了里面提供的部分Test案例,感觉还是很强大且值得实践的。关于HTML的代码片段主要关注的点有对于HTML标签的支持范围以及对于CSS样式和图片以及字体的支持,发现示例中对于常规的HTML标签均可支持,且对于外部的CSS文件以及<style>标签中的样式和标签内部的style属性定义的样式都可以支持。对于图片的支持也比较友好,主要是支持http在线图片和本地文件的图片以及base64文本的图片,对于字体库的支持也一样支持各种字体。

2.本站示例

本次示例将不再过多的介绍官网提供的示例,也不再准备独立或纯净的测试项目实现,仅将文章内容的html导出为pdf文档下载功能实现的代码逻辑介绍即可,所以以近期编写的文章《使用JAXB解析XML文件》为例(可自行全站搜一搜找到该篇文章),该文章包含范的知识点细节有:

(1)字体支持中文,文字颜色为红色(由style定义);

(2)支持超链接,生成的PDF文档可以点击超链接打开浏览器访问对应的http地址,也同样支持http地址的附件下载;

(3)支持图片显示,html代码中的图片地址为http请求的路径,导出时会被存储为本地的图片不再依赖网络环境;

注:(1)原始的文章内容预览页面引入了有许多全局的CSS定义,如对于图片缩放至最大宽度100%的设置,导出时只是将富文本编辑器中的内容进行了读取,与文字预览时定义的样式并非完全相同,所以导出的图片并不会缩放至PDF文档的100%宽度大小,也未尝试若定了此处大小的css是否能够支持;(2)原始内容中包含了有源代码的代码块高亮插件,是JS的二次渲染,所以导出PDF时的代码块部分只是会显示原始的代码,并不会高亮;

原始文字导出后的PDF文件参考:使用JAXB解析XML文件.zip

3.参考代码

参考代码给出的则比较简单,主要是itextpdf的maven坐标和功能中的导出下载逻辑,具体更多更细的代码转至本站开源的博客代码项目中查看。

<!-- itextpdf组件html转pdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>4.0.1</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>
package cn.chendd.blog.web.home.controller;

import ...;

/**
 * 文章导出Controller
 *
 * @author chendd
 * @date 2022/4/2 14:53
 */
@Api(value = "文章导出" , tags = "文章导出")
@ApiSort(60)
@Controller
@RequestMapping("/blog/export")
public class ArticleExportController extends BaseController {

    @Resource
    private ArticleExportService articleExportService;

    @Value("${server.http-port}")
    private Integer port;

    /**
     * 导出Pdf
     */
    @GetMapping("/pdf/{id}")
    @ApiOperation(value = "导出PDF" , notes = "导出文章内容数据")
    @ApiOperationSupport(order = 10)
    @ResponseBody
    public void exportPdf(@ApiParam(name = "id" , value = "文章id") @PathVariable Long id) throws IOException {
        ServletOutputStream outputStream = super.response.getOutputStream();
        ArticleSingleContentResult article = this.articleExportService.getArticleContent(id);
        try {
            String articleContent = article.getEditorContent();
            ///处理html代码中路径不为http开头,使用html2pdf组件自带的根路径处理方式解决
            /*{
                String basePath = Http.getBaseHttpPath(request);
                String filePrefix = "=\"/";
                //1.处理相关地址中的图片地址,把/开头的地址增加项目访问前缀
                articleContent = StringUtils.replace(articleContent , filePrefix , "=\"" + basePath + "/");
            }*/
            String fileName = DownloadNameUtil.browserDownloadName(article.getTitle() , request) + ".pdf";
            String attachment = ContentDisposition.builder("attachment").filename(fileName).build().toString();
            ConverterProperties converterProperties = new ConverterProperties();
            converterProperties.setBaseUri(this.getBaseHttpPath(request));
            converterProperties.setFontProvider(new DefaultFontProvider(true, true, true));
            converterProperties.setCharset(StandardCharsets.UTF_8.name());
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION , attachment);
            ///注意:此处未生成落地本地磁盘文件,故无法拿到准确的下载文件地址,除非落地本地文件,再定时清理
            ///response.setHeader(HttpHeaders.CONTENT_LENGTH , String.valueOf(articleContent.getBytes().length));
            HtmlConverter.convertToPdf(articleContent , outputStream , converterProperties);
        } catch (Exception e) {
            response.reset();
            response.setContentType(MediaType.TEXT_HTML_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            throw e;
        }
    }

    /**
     * 获取http协议下的路径,也可以使用https协议的路径
     * @param request request
     * @return http路径
     */
    private String getBaseHttpPath(HttpServletRequest request) {
        StringBuilder basePathBuilder = new StringBuilder();
        String portValue = "";
        if(port != 80) {
            portValue = ":" +  port;
        }
        basePathBuilder.append("http://")
                .append(request.getServerName()).append(portValue)
                .append(request.getContextPath());
        return basePathBuilder.toString();
    }

}

4.其它说明

itextpdf适用于小型企业、大型公司和ZF*机构,可以被视为首选的PDF技术,利用itextpdf提供的实现可以毫不费力的生成和操作PDF文档。itextpdf工具包提供了世界上文档最齐全、用途最广泛的PDF引擎之一(用Java和.NET编写),它不仅可以将PDF功能集成到您的工作流程中,还可以集成到您的应用程序、流程或产品中。本文主要是对itextpdf组件的html2pdf小小实践,更多还提供了pdfOCR、pdfOffice、pdf2Data等多个子项目,有机会再深入实践它们。早期的iText 5 Core已停止维护,不再开发,在新的项目中还是应与iText7的集成。

itext的官网地址为:https://itextpdf.com/,GitHub上的项目地址为:https://github.com/itext,以html2pdf为例,pdfHtml作为AGPL/商业软件获得双重许可,是免费/开源软件许可证,这并不意味着该软件是完全免费的!


 点赞


 发表评论

当前回复:作者

 评论列表


留言区