${title}
${notes}
${content}
在CMS项目中,网页的正文是用富文本编辑器来维护的,经过调研发现,将文章生成word版本时,需要使用doc4j来生成,核心代码如下:
### 对html进行标准化处理并增加字符集设置
Document document = org.jsoup.Jsoup.parse(htmlContent);
document.head().prepend("");
String normalizedHtmlContent = document.html();
### 将标准化后的html内容插入word文件
WordprocessingMLPackage aPackage = WordprocessingMLPackage.load(outputFile);
MainDocumentPart mainDocumentPart = aPackage.getMainDocumentPart();
mainDocumentPart.addAltChunk(AltChunkType.Html, normalizedHtmlContent.getBytes(Charsets.UTF_8));
aPackage.save(outputFile);
本来想将word转换成pdf, 经过失败了。不知道其他朋友有没有遇到过类似的需求场景,如果有的话,希望你能给大家分享一下。
最终转换思路,决定使用html来生成pdf, 经过一番调研,发现wkhtmltopdf还不错,能够较好地满足需求。
生成的pdf需要页眉页脚,其中页脚中的页码如果是奇数放在右边,如果是偶数放在左边,就像这样的效果:
通过阅读wkhtmltopdf文档发现,wkhtmltopdf在生成页脚时,使用GET请求获取页脚中页面的数据,同时会带上page等参数。考虑到要根据page来动态控制页脚中的页码,我决定使用freemarker来实现。因此需要在项目中引入freemaker并做好相关配置:
# 添加freemarker依赖
org.springframework.boot
spring-boot-starter-freemarker
# 在application.properties中配置相关参数
############################## freemarker START ##############################
spring.freemarker.enabled=true
spring.freemarker.template-loader-path=classpath:/templates/
spring.freemarker.suffix=.html
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.check-template-location=true
spring.freemarker.settings.tag_syntax=square_bracket
spring.freemarker.settings.template_update_delay=5
spring.freemarker.settings.defaultEncoding=UTF-8
spring.freemarker.settings.url_escaping_charset=UTF-8
spring.freemarker.settings.locale=zh_CN
spring.freemarker.settings.boolean_format=true,false
spring.freemarker.settings.datetime_format=yyyy-MM-dd HH:mm:ss
spring.freemarker.settings.date_format=yyyy-MM-dd
spring.freemarker.settings.time_format=HH:mm:ss
spring.freemarker.settings.number_format=0.######
spring.freemarker.settings.whitespace_stripping=true
spring.freemarker.settings.template_exception_handler=com.xxx.ow.staticpage.exception.FreemarkerExceptionHandler
############################## freemarker END ##############################
/**
* 规章详情
*
* @author : jamesfu
* @date : 21/2/2022 14:22
*/
@Controller
@RequestMapping("/api/page/ruleArticle")
@Slf4j
public class RuleArticleController {
/**
* pdf页眉
*
* @return 页面模板
*/
@GetMapping("pdfHeader")
public ModelAndView pdfHeader() {
return new ModelAndView("custom/article/规章详情-PDF-Header");
}
/**
* pdf页脚
*
* @return 页面模板
*/
@GetMapping("pdfFooter")
public ModelAndView pdfFooter(HttpServletRequest request, Model model) {
log.info("request query string is {}", request.getQueryString());
String strPage = request.getParameter("page");
log.info("page is {}", strPage);
if (strPage != null) {
int intPage = Integer.parseInt(strPage);
model.addAttribute("page", intPage);
} else {
model.addAttribute("page", 0);
}
return new ModelAndView("custom/article/规章详情-PDF-Footer");
}
/**
* pdf页面
*
* @return 页面模板
*/
@GetMapping("pdfBody")
public ModelAndView pdfBody(Model model) {
model.addAttribute("title","住房和城乡建设部关于修改《建设工程勘察质量管理办法》的决定");
model.addAttribute("notes","(2021年4月1日中华人民共和国住房和城乡建设部令第53号公布 自公布之日起施行)");
model.addAttribute("content","住房和城乡建设部决定对《建设工程勘察质量管理办法》(建设部令第115号,根据建设部令第163号修改)作如下修改:");
return new ModelAndView("custom/article/规章详情-PDF-Body");
}
}
规章详情页眉
住房和城乡建设部规章
规章详情页脚
[#if page % 2 == 0]
- ${page} -
[#else]
- ${page} -
[/#if]
规章详情正文
${title}
${notes}
${content}
/usr/local/bin/wkhtmltopdf --page-size A4 --header-spacing 3 --footer-spacing 6
--header-html 'http://127.0.0.1:18080/api/page/ruleArticle/pdfHeader'
--footer-html 'http://127.0.0.1:18080/api/page/ruleArticle/pdfFooter'
'http://127.0.0.1:18080/api/page/ruleArticle/pdfBody'
/Users/jamesfu/pdf/wengao022101.pdf
实现效果还是不错的。
com.github.jhonnymertz
java-wkhtmltopdf-wrapper
1.1.13-RELEASE
@Resource
private IFileUploadService fileUploadService;
@Resource
private SiteConfig siteConfig;
@Resource
private BusinessConfig businessConfig;
@Resource
private Configuration configuration;
/**
* 生成规章pdf文件
*
* @return pdf文件上传结果
*/
@Override
public FileUploadResult generatePdf(Map data) throws TemplateException, IOException {
//构造模板路径和输出文件路径
String articleTitle = data.get("title").toString();
String resultFileName = articleTitle + ".pdf";
String outputDir = siteConfig.getStaticTempDir() + File.separator + "ruleArticle";
String outputFilePath = outputDir + File.separator + resultFileName;
FileUtil.mkdir(outputDir);
File outputFile = FileUtil.file(outputFilePath);
String headerHtml = String.format("http://127.0.0.1:%s/api/page/ruleArticle/pdfHeader", businessConfig.getServerPort());
String footerHtml = String.format("http://127.0.0.1:%s/api/page/ruleArticle/pdfFooter", businessConfig.getServerPort());
String bodyHtml = generateBodyHtml(data);
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
String wkhtmltopdfCommand = businessConfig.getWkhtmltopdfCommand();
WrapperConfig wc = new WrapperConfig(wkhtmltopdfCommand);
Pdf pdf = new Pdf(wc);
pdf.addParam(new Param("--enable-local-file-access"));
pdf.addParam(new Param("--page-size", "A4"));
pdf.addParam(new Param("--header-spacing", "3"));
pdf.addParam(new Param("--footer-spacing", "6"));
pdf.addParam(new Param("--header-html", headerHtml));
pdf.addParam(new Param("--footer-html", footerHtml));
pdf.addPageFromString(bodyHtml);
pdf.saveAsDirect(outputFilePath);
fos.flush();
fos.close();
return fileUploadService.uploadFile(outputFile);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
FileUtil.del(outputFile);
}
}
private String generateBodyHtml(Map data) throws IOException, TemplateException {
Template template = configuration.getTemplate("/custom/article/规章详情-PDF-Body.html");
try (ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8)) {
template.process(data, writer);
return fileOutputStream.toString();
}
}
有服务器上运行fc-list命令查看安装的中文字体
fc-list :lang=zh-cn