WebページをPDFに簡単に変換するにはどうすればよいですか?



JavaハブではPDFドキュメントの操作に関する情報がほとんどないことは私にとって非常に予期していなかったため、個人的な経験から、サーブレットの例で、WebページをPDFドキュメントに変換することがどれほど簡単かを示したいと思います。



前文:
HTTPプロトコルを使用して指定されたWebページを取得し、それに基づいて本格的なPDFドキュメントを生成する単純なサーブレットを作成します。



使用されているライブラリ:


Maven構成のライブラリー説明(pom.xml)
<dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.0.4</version> </dependency> <dependency> <groupId>net.sourceforge.htmlcleaner</groupId> <artifactId>htmlcleaner</artifactId> <version>2.6.1</version> </dependency>
      
      







ページ形成:
最も重要なポイントの1つは、ページの形成です。 実際には、CSSを介してページ自体から、将来のPDFドキュメントのパラメーターが設定されます。



レイアウトを考慮してください。

page.jsp
 <%@ page import="java.util.Date" %> <%@ page import="java.text.SimpleDateFormat" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%! private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); %> <html> <head> <title></title> <style> @font-face { font-family: "HabraFont"; src: url(http://localhost:8080/resources/fonts/tahoma.ttf); -fs-pdf-font-embed: embed; -fs-pdf-font-encoding: Identity-H; } @page { margin: 0px; padding: 0px; size: A4 portrait; } @media print { .new_page { page-break-after: always; } } body { background-image: url(http://localhost:8080/resources/images/background.png); } body *{ padding: 0; margin: 0; } * { font-family: HabraFont; } #block { width: 90%; margin: auto; background-color: white; border: dashed #dbdbdb 1px; } #logo { margin-top: 5px; width: 100%; text-align: center; border-bottom: dashed #dbdbdb 1px; } #content { padding-left: 10px; } </style> </head> <body> <div id="block"> <div id="logo"><img src="http://localhost:8080/resources/images/habra-logo.png"></div> <div id="content"> , !  : <%=sdf.format(new Date())%> <div class="new_page"> </div>  ! </div> </div> </body> </html>
      
      







ここで、いくつかの点について説明します。 まず第一に、最も重要なこと: すべてのパスは絶対パスでなければなりません ! 写真、スタイル、フォントアドレスなど、すべてに絶対パスを記述する必要があります。 次に、CSSルール(@記号で始まるもの)を見ていきましょう。

@ font-faceは、PDFジェネレーターに取得するフォントとその取得元を指示するルールです。 問題は、PDFドキュメントを生成するライブラリにキリル文字を含むフォントが含まれていないことです。 そのため、このように、標準フォント(Arial、Verdana、Tahomaなど)であっても、ページで使用されるすべてのフォントを決定する必要があります。そうしないと、ドキュメントにキリル文字が表示されない危険があります。

「-fs-pdf-font-embed:embed;」などのプロパティに注意してください。 および「-fs-pdf-font-encoding:Identity-H;」、これらのプロパティは必須です。追加するのを忘れないでください。

@ページは、PDFドキュメントのパディングとそのサイズを設定するルールです。 ここでは、ページサイズA3を指定した場合(実際に示すように、ページがドキュメントの幅に収まらないため、これが必要になることがよくあります)、これはユーザーがドキュメントを印刷する必要があることを意味しません(必要に応じて)むしろ、A3形式では、すべてのコンテンツのみが、必要に応じて比例的に縮小/拡大されます(通常はA4)。 つまり sizeプロパティの値については懐疑的ですが、それがあなたにとって重要な役割を果たす可能性があることに注意してください。

@ media-特定のタイプのデバイス用のCSSクラスを作成できるルール。この場合は「印刷」です。 このルール内でクラスを作成し、その後PDFドキュメントジェネレーターが新しいページを作成します。



サーブレット:
次に、生成されたPDFドキュメントを返すサーブレットを作成します。

Pdfservlet.java
 package ru.habrahabr.web_to_pdf.servlets; import org.htmlcleaner.CleanerProperties; import org.htmlcleaner.HtmlCleaner; import org.htmlcleaner.PrettyXmlSerializer; import org.htmlcleaner.TagNode; import org.xhtmlrenderer.pdf.ITextRenderer; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; /** * Date: 31.03.2014 * Time: 9:33 * * @author Ruslan Molchanov (ruslanys@gmail.com) */ public class PdfServlet extends HttpServlet { private static final String PAGE_TO_PARSE = "http://localhost:8080/page.jsp"; private static final String CHARSET = "UTF-8"; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { resp.setContentType("application/pdf"); byte[] pdfDoc = performPdfDocument(PAGE_TO_PARSE); resp.setContentLength(pdfDoc.length); resp.getOutputStream().write(pdfDoc); } catch (Exception ex) { resp.setContentType("text/html"); PrintWriter out = resp.getWriter(); out.write("<strong>Something wrong</strong><br /><br />"); ex.printStackTrace(out); ex.printStackTrace(); } } /** * ,  PDF . * @param path    * @return PDF  * @throws Exception */ private byte[] performPdfDocument(String path) throws Exception { //  HTML   String html = getHtml(path); // ,      HTML  ByteArrayOutputStream out = new ByteArrayOutputStream(); //  HTML  /*    ,        */ HtmlCleaner cleaner = new HtmlCleaner(); CleanerProperties props = cleaner.getProperties(); props.setCharset(CHARSET); TagNode node = cleaner.clean(html); new PrettyXmlSerializer(props).writeToStream(node, out); //  PDF   HTML  ITextRenderer renderer = new ITextRenderer(); renderer.setDocumentFromString(new String(out.toByteArray(), CHARSET)); renderer.layout(); /* ,       PDF , ,   *     ,    PDF , *    ,     */ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); renderer.createPDF(outputStream); //   renderer.finishPDF(); out.flush(); out.close(); byte[] result = outputStream.toByteArray(); outputStream.close(); return result; } private String getHtml(String path) throws IOException { URLConnection urlConnection = new URL(path).openConnection(); ((HttpURLConnection) urlConnection).setInstanceFollowRedirects(true); HttpURLConnection.setFollowRedirects(true); boolean redirect = false; // normally, 3xx is redirect int status = ((HttpURLConnection) urlConnection).getResponseCode(); if (HttpURLConnection.HTTP_OK != status && (HttpURLConnection.HTTP_MOVED_TEMP == status || HttpURLConnection.HTTP_MOVED_PERM == status || HttpURLConnection.HTTP_SEE_OTHER == status)) { redirect = true; } if (redirect) { // get redirect url from "location" header field String newUrl = urlConnection.getHeaderField("Location"); // open the new connnection again urlConnection = new URL(newUrl).openConnection(); } urlConnection.setConnectTimeout(30000); urlConnection.setReadTimeout(30000); BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), CHARSET)); StringBuilder sb = new StringBuilder(); String line; while (null != (line = in.readLine())) { sb.append(line).append("\n"); } return sb.toString().trim(); } @Override public String getServletInfo() { return "The servlet that generate and returns pdf file"; } }
      
      







ところで、これらの目的のためにサーブレットを記述する必要はありません。PDF文書をファイルに保存するコンソールアプリケーションにさえ、このサーブレットのロジックを転送できます。 お気づきかもしれませんが、サーブレットでは、構成、変更、補足などを行う必要はありません。 (まあ、ページへのパスと、おそらくエンコーディングを除いて)、それぞれ、PDFドキュメントの準備に関するすべての作業は非常に簡単で、ビュー内でのみ行われます。



最後に、このPDFドキュメントのようなものを取得する必要があります: github.com/ruslanys/example-web-to-pdf/blob/master/web-to-pdf-example.pdf

ドキュメントに情報を少し追加し(Habrのメインページを解析)、次のドキュメントを取得しました: github.com/ruslanys/sample-html-to-pdf/blob/master/web-to-pdf-habra.pdf



ソースへのリンク: github.com/ruslanys/sample-html-to-pdf



PS原則として、この例に基づいて、任意のページアドレスでPDFドキュメントを作成するサービス全体を作成できます。 行う必要がある唯一のことは、ルールに従ってページのHTMLコードを取得することです。 まず、すべての相対パスを絶対パスに書き換える必要があります(これは難しいことではありません)。また、何らかのロジックに従って、ドキュメントのサイズを設定します。



All Articles