一、背景

项目中需要将某数据显示的内容,提供一个下载 DOCX 与 PDF 功能。在分析阶段发现 docx4j(http://www.docx4java.org/trac/docx4j)提供了转换功能。在调试开发时遇到了 HTML 格式兼容,样式丢失,PDF 中文字体等问题。

二、分析

docx4j-ImportXHTML(https://github.com/plutext/docx4j-ImportXHTML),从名称上一看就知道这个只支持 XHTML。如果是非 XHTML 格式,解析就有问题。

所以在样例中使用了jsoup(http://jsoup.org/)将 HTML 统一转换为 XHTML,并去掉不需要的一些内容(如:script)。这时再调用 docx4j-ImportXHTML 就可以正常解析。

注:这种转换不适用于常规 HTML 页面,转换过程中会丢失样式造成混乱。在这里想要做的是一种以特定 HTML 格式编写页面模板转出 DOCX 与 PDF 的方式。

三、样例程序

样例程序中有很多注释,这理就不再深入描述。该程序支持 Linux 环境。

1、主流程

a、jsoup 抓取指定 URL 的内容

b、使用 jsoup 清理内容,转为 XHTML

c、调用 docx4j-ImportXHTML,生成 WordprocessingMLPackage 对象(docx4j)

d、另存为 DOCX 与 PDF

2、POM 文件

这里使用了 Jetty,主要作用是测试时充当假 HTTP 服务器。

直接运行 mvn clean test 就可以看到转换效果。

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.noahx

html2docx

1.0.0-SNAPSHOT

org.docx4j

docx4j-ImportXHTML

3.2.2

slf4j-log4j12

org.slf4j

log4j

log4j

org.jsoup

jsoup

1.8.1

org.slf4j

slf4j-simple

1.7.10

test

org.eclipse.jetty

jetty-server

9.2.9.v20150224

test

3、TestHtmlConverter 单元测试类

该类创建模拟 HTTP 服务器,调用转换类将 HTML 内容转换为 DOCX 与 PDF,并调用操作系统打开文件操作。

出于调试目的,日志输出级别为 DEBUG,会产生大量日志。实际运行时可以提高日志级别。

package org.noahx.html2docx;

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

import org.slf4j.impl.SimpleLogger;

import java.awt.*;

import java.io.File;

/**

* Created by noah on 3/12/15.

*/

public class TestHtmlConverter {

private static HtmlServer htmlServer = new HtmlServer();

@BeforeClass

public static void before() {

System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG");

htmlServer.start();

}

@AfterClass

public static void after() {

htmlServer.stop();

}

@Test

public void test() throws Exception {

HtmlConverter converter = new HtmlConverter();

String url = "http://127.0.0.1:" + htmlServer.getPort() + "/report.html"; //输入要转换的网址

File fileDocx = converter.saveUrlToDocx(url);

File filePdf = converter.saveUrlToPdf(url);

Desktop.getDesktop().open(fileDocx); //由操作系统打开

Desktop.getDesktop().open(filePdf);

}

}

4、HTML 样本文件(report.html)

样式问题请查看注释。

测试标题

body {

font-family: SimSun;

}

.tb {

border-collapse: collapse;

empty-cells: show;

width: 100%; /*竖版时100%宽度不正确*/

}

.tb th {

text-align: center;

border: 1px solid #000000; /* pdf 输出时边颜色受 color 影响,所以指定 #000000 */

}

.tb td {

border: 1px solid #000000; /* pdf 输出时边颜色受 color 影响,所以指定 #000000 */

}

p {

/*不支持 text-indent 样式,用中文全角空格( ) */

/*text-indent: 2em;*/

}

标题1:大家好

标题2:大家好

标题3:大家好

  这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。这是一个中文段落。

a
第一列 第二列 第三列 第四列
abc efg efg efg
abc efg efg efg

表1

第一列 第二列 第三列 第四列
abc efg efg efg
abc efg efg efg

图1

图2

图3

5、主转换程序(HtmlConverter)

package org.noahx.html2docx;

import org.docx4j.Docx4J;

import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;

import org.docx4j.fonts.IdentityPlusMapper;

import org.docx4j.fonts.Mapper;

import org.docx4j.fonts.PhysicalFont;

import org.docx4j.fonts.PhysicalFonts;

import org.docx4j.jaxb.Context;

import org.docx4j.model.structure.PageSizePaper;

import org.docx4j.openpackaging.packages.WordprocessingMLPackage;

import org.docx4j.wml.RFonts;

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.jsoup.nodes.Entities;

import org.jsoup.select.Elements;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.io.File;

import java.io.OutputStream;

import java.net.URL;

/**

* Created by noah on 3/10/15.

*/

public class HtmlConverter {

/**

* 输出文件名

*/

public final String OUT_FILENAME = "OUT_ConvertInXHTMLURL";

private final Logger logger = LoggerFactory.getLogger(this.getClass());

/**

* 将页面保存为 docx

*

* @param url

* @return

* @throws Exception

*/

public File saveUrlToDocx(String url) throws Exception {

return saveDocx(url2word(url));

}

/**

* 将页面保存为 pdf

*

* @param url

* @return

* @throws Exception

*/

public File saveUrlToPdf(String url) throws Exception {

return savePdf(url2word(url));

}

/**

* 将页面转为 {@link org.docx4j.openpackaging.packages.WordprocessingMLPackage}

*

* @param url

* @return

* @throws Exception

*/

public WordprocessingMLPackage url2word(String url) throws Exception {

return xhtml2word(url2xhtml(url));

}

/**

* 将 {@link org.docx4j.openpackaging.packages.WordprocessingMLPackage} 存为 docx

*

* @param wordMLPackage

* @return

* @throws Exception

*/

public File saveDocx(WordprocessingMLPackage wordMLPackage) throws Exception {

File file = new File(genFilePath() + ".docx");

wordMLPackage.save(file); //保存到 docx 文件

if (logger.isDebugEnabled()) {

logger.debug("Save to [.docx]: {}", file.getAbsolutePath());

}

return file;

}

/**

* 将 {@link org.docx4j.openpackaging.packages.WordprocessingMLPackage} 存为 pdf

*

* @param wordMLPackage

* @return

* @throws Exception

*/

public File savePdf(WordprocessingMLPackage wordMLPackage) throws Exception {

File file = new File(genFilePath() + ".pdf");

OutputStream os = new java.io.FileOutputStream(file);

Docx4J.toPDF(wordMLPackage, os);

os.flush();

os.close();

if (logger.isDebugEnabled()) {

logger.debug("Save to [.pdf]: {}", file.getAbsolutePath());

}

return file;

}

/**

* 将 {@link org.jsoup.nodes.Document} 对象转为 {@link org.docx4j.openpackaging.packages.WordprocessingMLPackage}

* xhtml to word

*

* @param doc

* @return

* @throws Exception

*/

protected WordprocessingMLPackage xhtml2word(Document doc) throws Exception {

WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(PageSizePaper.valueOf("A4"), true); //A4纸,//横版:true

configSimSunFont(wordMLPackage); //配置中文字体

XHTMLImporterImpl xhtmlImporter = new XHTMLImporterImpl(wordMLPackage);

wordMLPackage.getMainDocumentPart().getContent().addAll( //导入 xhtml

xhtmlImporter.convert(doc.html(), doc.baseUri()));

return wordMLPackage;

}

/**

* 将页面转为{@link org.jsoup.nodes.Document}对象,xhtml 格式

*

* @param url

* @return

* @throws Exception

*/

protected Document url2xhtml(String url) throws Exception {

Document doc = Jsoup.connect(url).get(); //获得

if (logger.isDebugEnabled()) {

logger.debug("baseUri: {}", doc.baseUri());

}

for (Element script : doc.getElementsByTag("script")) { //除去所有 script

script.remove();

}

for (Element a : doc.getElementsByTag("a")) { //除去 a 的 onclick,href 属性

a.removeAttr("onclick");

a.removeAttr("href");

}

Elements links = doc.getElementsByTag("link"); //将link中的地址替换为绝对地址

for (Element element : links) {

String href = element.absUrl("href");

if (logger.isDebugEnabled()) {

logger.debug("href: {} -> {}", element.attr("href"), href);

}

element.attr("href", href);

}

doc.outputSettings()

.syntax(Document.OutputSettings.Syntax.xml)

.escapeMode(Entities.EscapeMode.xhtml); //转为 xhtml 格式

if (logger.isDebugEnabled()) {

String[] split = doc.html().split("\n");

for (int c = 0; c < split.length; c++) {

logger.debug("line {}:\t{}", c + 1, split[c]);

}

}

return doc;

}

/**

* 为 {@link org.docx4j.openpackaging.packages.WordprocessingMLPackage} 配置中文字体

*

* @param wordMLPackage

* @throws Exception

*/

protected void configSimSunFont(WordprocessingMLPackage wordMLPackage) throws Exception {

Mapper fontMapper = new IdentityPlusMapper();

wordMLPackage.setFontMapper(fontMapper);

String fontFamily = "SimSun";

URL simsunUrl = this.getClass().getResource("/org/noahx/html2docx/simsun.ttc"); //加载字体文件(解决linux环境下无中文字体问题)

PhysicalFonts.addPhysicalFont(fontFamily, simsunUrl);

PhysicalFont simsunFont = PhysicalFonts.get(fontFamily);

fontMapper.put(fontFamily, simsunFont);

RFonts rfonts = Context.getWmlObjectFactory().createRFonts(); //设置文件默认字体

rfonts.setAsciiTheme(null);

rfonts.setAscii(fontFamily);

wordMLPackage.getMainDocumentPart().getPropertyResolver()

.getDocumentDefaultRPr().setRFonts(rfonts);

}

/**

* 生成文件位置

*

* @return

*/

protected String genFilePath() {

return System.getProperty("user.dir") + "/" + OUT_FILENAME;

}

}

四、转换效果

1、DOCX转换效果

2、PDF转换效果

五、源码下载

java word转pdf dox4j,使用 docx4j 将 Web 页面转换为 DOCX 与 PDF 格式相关推荐

  1. flyingsaucer转换多个html,java - 使用FlyingSaucer将包含阿拉伯字符的HTML页面转换为PDF - 堆栈内存溢出...

    我想使用FlyingSaucer将包含阿拉伯字符的HTML页面转换为PDF文件,但生成的PDF不包含组合字符并向后打印输出. HTML: جميع الحقوق Java摘录: String inpu ...

  2. Word中的图片保存为无损图,转换为高质量pdf(无压缩)

    首先确保图片经过压缩后,在Word中如何保证图片导入后分辨率不降低 教程:https://jingyan.baidu.com/article/fdffd1f8ef5effb3e98ca180.html ...

  3. 方便、免费的PDF在线处理网站汇总:PDF合并、文字编辑、页面提取与删除、格式转换…

      本文介绍几个方便.免费.好用的PDF在线处理网站.   在工作与学习过程中,经常会需要对PDF进行一些基本处理,例如文件合并.文件格式转换.页面顺序修改等等:尽管这些需求可以通过许多成熟的PDF处 ...

  4. java 高德地图路线规划_高德地图 web 页面里的出行路线规划

    高德地图的引入,有两种方式,第一种在引入链接里直接添加 plugin 参数: 第二种是引入链接里不添加  plugin 参数,而是在在使用插件之前,使用AMap.service方法加载插件,然后在回调 ...

  5. drawboard pdf拆分文件_PDF处理神器,几秒钟搞定格式转换+压缩+加水印+解密!

    PDF对于一个科研学习/工作者来说几乎每天都会接触,尤其是PDF格式转换的时候不知道怎么办,还有些文件加密了只能看不能编辑,有些几十页甚至几百页的文件每次翻看起来都特别麻烦,想防盗给自己的pdf文件加 ...

  6. java 采集 cms_开源 java CMS - FreeCMS2.6 Web页面信息采集

    java开源论坛系统http://javabbs.javaz.cn Web页面信息采集 从FreeCMS 2.1开始支持 通过简单配置即可抓取目标网页信息,支持增量式采集.关键字替换.定时采集,同一采 ...

  7. java word转pdf,docx4j转pdf,docx4j导出pdf乱码,docx4j导出pdf丢失插画和图片,aspose将word转pdf 一共两种方法

    前言:一共有docx4j转pdf,aspose转pdf两种方式,不需要设置模板!!! java转pdf目前本人使用有两种方法,下面是方法代码 ps:因为本人是云桌面开发,所以只作截图,具体代码需要自己 ...

  8. java word转pdf三种方法(附有需要的jar)

    一.jacob 1.jar下载 jacob.jar和jacob-1.17-x64.dll下载 提取码:0121 2.在jdk/bin目录下引入.dll文件(64位:jacob-1.17-x64.dll ...

  9. java word转pdf linux_Linux平台中使用PHP把word转pdf的实现方法

    Linux平台中使用PHP把word转pdf的实现方法 1.ubantu下安装libreoffice sudo apt-get install libreoffice 2.命令行执行word转pdf ...

  10. java word转pdf_java里实现Word转PDF的几种方案

    一.libreOffice 与openOffice类似,但比openOffice稳定. 优点:样式稳定 缺点:性能较差 调用方式:windows:1 2 3 4 5 6 7 8 9 10 11 12 ...

最新文章

  1. 简述nodejs、npm及其模块在windows下的安装与配置
  2. 如何修改 远程桌面的 默认端口号 3389
  3. 一键安装GitLab7在RHEL6.4上
  4. CentOS6.4 利用sendEmail发邮件
  5. 根据ip查经纬度软件_f.lux - 必须推荐给大家的开源免费的护眼软件
  6. SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(开发部署)
  7. 搜索2.0:利用用户点击记录改善搜索结果
  8. 光学模拟 Android,基于Android平台的光学字符识别应用的设计与实现
  9. mysql 字段操作
  10. linux下eclipse进行ndk调试,超简单,写的超清晰
  11. 离线语音识别技术品鉴——功能不同各有千秋
  12. Sengled Snap带摄像头的智能灯泡
  13. CentOS7中rpm,yum软件安装命令
  14. win10电脑切换窗口输入法总是变换怎么办?
  15. 【总结】浪潮杯第七届ACM山东省省赛山师场总结
  16. 暴雪即将公布《暗黑破坏神3》新职业
  17. containsAll和contains
  18. Android GMS认证项总结
  19. Blender雕刻模块:如何在雕刻过程中无缝合并物体
  20. 字节跳动的真实工作体验

热门文章

  1. Mysql数据库误删除数据恢复方案
  2. 三维扫描仪为媒,虚拟试衣间下嫁普通制衣生产厂家
  3. python 获取像素颜色_python如何读取像素值
  4. Pix2Pix代码解析
  5. 仿真工具NS3的基本知识
  6. TP-LINK无线上网短信Wifi认证配置流程
  7. ARINC429总线基础
  8. JavaSwing订餐管理系统
  9. 用python实现整段翻译论文
  10. Python实现Word文档翻译