Java中使用Flying Saucer,OpenPDF将HTML转换为PDF
在本教程中,我们将了解如何使用Flying Saucer,OpenPDF和jsoup在Java中将HTML转换为PDF。
有关使用PDFBox将HTML转换为PDF的信息,请检查这篇文章使用Openhtmltopdf,PDFBox将Java中的HTML转换为PDF。
使用Flying Saucer将HTML转换为PDF –工作原理
Flying Saucer呈现格式良好的XML,这意味着它将XML文件作为输入,使用CSS应用格式和样式,并生成该XML的呈现形式作为输出。因此,将HTML转换为PDF的步骤如下:
- 第一步是确保我们使用jsoup将HTML转换为XHTML的格式正确的HTML。
- Flying Saucer生成XHTML和CSS的呈现形式。
- OpenPDF用于从该表示形式生成PDF文档。
OpenPDF是iText版本4的分支,它是具有LGPL和MPL许可证的开源软件。在这篇文章中阅读有关OpenPDF的更多信息https://theitroad.com/java-programs/generating-pdf-java-using-openpdf-tutorial/
Maven依赖
jsoup和Flying Saucer的Apache Maven依赖关系如下
<dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.13.1</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf-openpdf</artifactId> <version>9.1.20</version> </dependency> <!-- Dependency for Apache commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
提到的Flying Saucer依赖项将获得OpenPDF所需的jar以及Flying Saucer核心(flying-saucer-core-9.1.20.jar)。
使用Flying Saucer和OpenPDF Java程序将HTML转换为PDF
将HTML转换为PDF时,我遇到的三个问题是
- 如何使用<img src ="" ..>标签在PDF中显示以HTML格式显示的图像。
- 如何添加任何特定的网络字体。
- 如何确保HTML中使用的外部CSS也可以用来设置生成的PDF的样式。
示例程序使用的文件夹结构如下所示。在OpenPDF文件夹中,我们有HTML文件,真型字体文件和png图像文件,而OpenPDF / css文件夹中有css文件。
-OpenPDF MyPage.html Gabriola.ttf Image OpenPDF.png --css mystyles.css
MyPage.html
<html lang="en"> <head> <title>MyPage</title> <style type="text/css"> body{background-color: powderblue;} </style> <link href="css/mystyles.css" rel="stylesheet" > </head> <body> <h1>Convert HTML to PDF</h1> <p>Here is an embedded image</p> <img src="F:\theitroad\Java\Java Programs\PDF using Java\OpenPDF\Image OpenPDF.png" width="250" height="150"> <p style="color:red">Styled text using Inline CSS</p> <i>This is italicised text</i> <p class="fontclass">This text uses the styling from font face font</p> <p class="myclass">This text uses the styling from external CSS class</p> </body> </html>
mystyles.css
在css中,@ font-face规则用于指定字体和可以找到它的URL。使用@page规则指定在打印文档时要使用的CSS属性。
@font-face { font-family: myFont; src: url("../Gabriola.ttf"); } .fontclass{ font-family: myFont; } @Page { size: 8.5in 11in; margin: 1in; } .myclass{ font-family: Helvetica, sans-serif; font-size:25; font-weight: normal; color: blue; }
这就是在Chrome浏览器中呈现HTML的方式。
现在我们的工作是编写一个Java程序,通过使用相同的图像源,使用相同的外部CSS并添加CSS @ font-face规则中使用的字体,可以将该HTML转换为PDF。
为了使图像在转换为PDF时正常工作,对我而言,有效的方法是实现自己的ReplacedElementFactory,该工厂将图像转换为字节,然后使用它来创建ImageElement。这里有一个讨论。
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.w3c.dom.Element; import org.xhtmlrenderer.extend.FSImage; import org.xhtmlrenderer.extend.ReplacedElement; import org.xhtmlrenderer.extend.ReplacedElementFactory; import org.xhtmlrenderer.extend.UserAgentCallback; import org.xhtmlrenderer.layout.LayoutContext; import org.xhtmlrenderer.pdf.ITextFSImage; import org.xhtmlrenderer.pdf.ITextImageElement; import org.xhtmlrenderer.render.BlockBox; import org.xhtmlrenderer.simple.extend.FormSubmissionListener; import com.lowagie.text.BadElementException; import com.lowagie.text.Image; public class ImageReplacedElementFactory implements ReplacedElementFactory { @Override public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) { Element e = box.getElement(); if (e == null) { return null; } String nodeName = e.getNodeName(); if (nodeName.equals("img")) { String attribute = e.getAttribute("src"); FSImage fsImage; try { fsImage = imageForPDF(attribute, uac); } catch (BadElementException e1) { fsImage = null; } catch (IOException e1) { fsImage = null; } if (fsImage != null) { if (cssWidth != -1 || cssHeight != -1) { //System.out.println("scaling"); fsImage.scale(cssWidth, cssHeight); }else { fsImage.scale(250, 150); } return new ITextImageElement(fsImage); } } return null; } protected FSImage imageForPDF(String attribute, UserAgentCallback uac) throws IOException, BadElementException { InputStream input = null; FSImage fsImage; input = new FileInputStream(attribute); final byte[] bytes = IOUtils.toByteArray(input); final Image image = Image.getInstance(bytes); fsImage = new ITextFSImage(image); return fsImage; } @Override public void reset() { // TODO Auto-generated method stub } @Override public void remove(Element e) { // TODO Auto-generated method stub } @Override public void setFormSubmissionListener(FormSubmissionListener listener) { // TODO Auto-generated method stub } }
以下Java程序用于使用HTML作为源来生成PDF
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.FileSystems; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.xhtmlrenderer.layout.SharedContext; import org.xhtmlrenderer.pdf.ITextRenderer; public class HTMLToPDF { public static void main(String[] args) { try { // Source HTML file File inputHTML = new File("F:\theitroad\Java\Java Programs\PDF using Java\OpenPDF\MyPage.html"); // Generated PDF file name File outputPdf = new File("F:\theitroad\Java\Java Programs\PDF using Java\OpenPDF\Output.pdf"); //Convert HTML to XHTML String xhtml = htmlToXhtml(inputHTML); System.out.println("Converting to PDF..."); xhtmlToPdf(xhtml, outputPdf); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static String htmlToXhtml(File inputHTML) throws IOException { Document document = Jsoup.parse(inputHTML, "UTF-8"); System.out.println("parsing ..."); document.outputSettings().syntax(Document.OutputSettings.Syntax.xml); System.out.println("parsing done ..."); return document.html(); } private static void xhtmlToPdf(String xhtml, File outputPdf) throws IOException { ITextRenderer renderer = new ITextRenderer(); SharedContext sharedContext = renderer.getSharedContext(); sharedContext.setPrint(true); sharedContext.setInteractive(false); sharedContext.setReplacedElementFactory(new ImageReplacedElementFactory()); sharedContext.getTextRenderer().setSmoothingThreshold(0); renderer.getFontResolver().addFont("F:\theitroad\Java\Java Programs\PDF using Java\OpenPDF\Gabriola.ttf", true); String baseUrl = FileSystems.getDefault() .getPath("F:\", "theitroad\Java\", "Java Programs\PDF using Java\OpenPDF") .toUri() .toURL() .toString(); renderer.setDocumentFromString(xhtml, baseUrl); renderer.layout(); OutputStream outputStream = new FileOutputStream(outputPdf); renderer.createPDF(outputStream); System.out.println("PDF creation completed"); // put this in finally outputStream.close(); } }
在程序中,要注意的一些重要点是:
1.sharedContext.setReplacedElementFactory(new ImageReplacedElementFactory());
设置ReplacedElementFactory的自定义实现。
2.在方法renderer.setDocumentFromString(xhtml,baseUrl);
中,将baseURL作为第二个参数传递。使用此语句创建URL
String baseUrl = FileSystems.getDefault().getPath("F:\", "theitroad\Java\", "Java Programs\PDF using Java\OpenPDF").toUri().toURL().toString();
3.如果我们在HTML中注意到css的路径是相对路径。通过设置第二点中给出的baseURL,将能够解析此相对路径,这有助于在生成PDF时使用外部CSS。
4.使用此语句注册其他字体
renderer.getFontResolver().addFont("F:\theitroad\Java\Java Programs\PDF using Java\OpenPDF\Gabriola.ttf", true);
生成的PDF
参考:https://flyingsaucerproject.github.io/flyingsaucer/r8/guide/users-guide-R8.html