通過java調用系統中的wkhtmltopdf

通過java調用系統中的wkhtmltopdf

希望達到脫離瀏覽器將頁面導成 pdf 的效果, 於是利用 wkhtmltopdf 的將 urlpdf 的能力實現
利用 java 代碼觸發 wkhtmltopdf

注意事項

有幾點東西要注意一下

  1. 中文字體問題, 大多數 linux 系統是默認不帶中文字體的, 需要自己手動安裝一下
  2. 這個操作相對還是比較耗時的(特別是網絡開銷), 如果併發量比較大需要關注下性能問題
  3. 剩下就不細講了, 看下面的源碼應該都明白了

參數說明

命令格式

wkhtmltopdf [OPTIONS]... <input file> [More input files] <output file>

常規選項

  • --allow <path>: 允許加載從指定的文件夾中的文件或文件(可重複)
  • --book: 設置一會打印一本書的時候,通常設置的選項
  • --collate: 打印多份副本時整理
  • --cookie <name> <value>: 設置一個額外的cookie(可重複)
  • --cookie-jar <path>: 讀取和寫入的Cookie,並在提供的cookie jar文件
  • --copies <number>: 複印打印成pdf文件數(默認爲1)
  • --cover* <url>: 使用HTML文件作爲封面。它會帶頁眉和頁腳的TOC之前插入
  • --custom-header <name> <value>: 設置一個附加的HTTP頭(可重複)
  • --debug-javascript: 顯示的javascript調試輸出
  • --default-header: 添加一個缺省的頭部,與頁面的左邊的名稱,頁面數到右邊,例如:–header-left '[webpage]' --header-right '[page]/[toPage]' --header-line
  • --disable-external-links: 禁止生成鏈接到遠程網頁
  • --disable-internal-links: 禁止使用本地鏈接
  • `–disable-javascript 禁止讓網頁執行JavaScript
  • --disable-pdf-compression: 禁止在PDF對象使用無損壓縮
  • --disable-smart-shrinking: 禁止使用WebKit的智能戰略收縮,使像素/ DPI比沒有不變
  • --disallow-local-file-access: 禁止允許轉換的本地文件讀取其他本地文件,除非explecitily允許用 --allow
  • --dpi <dpi>: 顯式更改DPI(這對基於X11的系統沒有任何影響)
  • --enable-plugins: 啓用已安裝的插件(如Flash
  • --encoding <encoding>: 設置默認的文字編碼
  • --extended-help: 顯示更廣泛的幫助,詳細介紹了不常見的命令開關
  • --forms: 打開HTML表單字段轉換爲PDF表單域
  • --grayscale: PDF格式將在灰階產生
  • --help: Display help
  • --htmldoc: 輸出程序HTML幫助
  • --ignore-load-errors: 忽略claimes加載過程中已經遇到了一個錯誤頁面
  • --lowquality: 產生低品質的PDF/ PS。有用縮小結果文檔的空間
  • --manpage: 輸出程序手冊頁
  • --margin-bottom <unitreal>: 設置頁面下邊距 (default 10mm)
  • --margin-left <unitreal>: 將左邊頁邊距 (default 10mm)
  • --margin-right <unitreal>: 設置頁面右邊距 (default 10mm)
  • --margin-top <unitreal>: 設置頁面上邊距 (default 10mm)
  • --minimum-font-size <int>: 最小字體大小 (default 5)
  • --no-background: 不打印背景
  • --orientation <orientation>: 設置方向爲橫向或縱向
  • --page-height <unitreal>: 頁面高度 (default unit millimeter)
  • --page-offset* <offset>: 設置起始頁碼 (default 1)
  • --page-size <size>: 設置紙張大小: A4, Letter, etc.
  • --page-width <unitreal>: 頁面寬度 (default unit millimeter)
  • --password <password>: HTTP驗證密碼
  • --post <name> <value>: Add an additional post field (repeatable)
  • --post-file <name> <path>: Post an aditional file (repeatable)
  • --print-media-type: 使用的打印介質類型,而不是屏幕
  • --proxy <proxy>: 使用代理
  • --quiet: Be less verbose
  • --read-args-from-stdin: 讀取標準輸入的命令行參數
  • --readme: 輸出程序自述
  • --redirect-delay <msec>: 等待幾毫秒爲JS-重定向(default 200)
  • --replace* <name> <value>: 替換名稱,值的頁眉和頁腳(可重複)
  • --stop-slow-scripts: 停止運行緩慢的JavaScripts
  • --title <text>: 生成的PDF文件的標題(第一個文檔的標題使用,如果沒有指定)
  • --toc: 插入的內容的表中的文件的開頭
  • --use-xserver: 使用X服務器(一些插件和其他的東西沒有X11可能無法正常工作)
  • --user-style-sheet <url>: 指定用戶的樣式表,加載在每一頁中
  • --username <username>: HTTP認證的用戶名
  • --version: 輸出版本信息退出
  • --zoom <float>: 使用這個縮放因子 (default 1)

頁眉和頁腳選項

  • --header-center** <text>: (設置在中心位置的頁眉內容)
  • --header-font-name* <name>: (default Arial) (設置頁眉的字體名稱)
  • --header-font-size* <size>: (設置頁眉的字體大小)
  • --header-html <url>: (添加一個HTML頁眉,後面是網址)
  • --header-left <text>: (左對齊的頁眉文本)
  • --header-line: (顯示一條線在頁眉下)
  • --header-right* <text>: (右對齊頁眉文本)
  • --header-spacing: <real>: (設置頁眉和內容的距離,默認0)
  • --footer-center: <text>: (設置在中心位置的頁腳內容)
  • --footer-font-name* <name>: (設置頁腳的字體名稱)
  • --footer-font-size* <size>: (設置頁腳的字體大小default 11)
  • --footer-html <url>: (添加一個HTML頁腳,後面是網址)
  • --footer-left <text>: (左對齊的頁腳文本)
  • --footer-line: 顯示一條線在頁腳內容上)
  • --footer-right <text>: (右對齊頁腳文本)
  • --footer-spacing <real>: (設置頁腳和內容的距離)

###表內容選項中

  • --toc-depth <level>: Set the depth of the toc (default 3)
  • --toc-disable-back-links: Do not link from section header to toc
  • --toc-disable-links: Do not link from toc to sections
  • --toc-font-name* <name>: Set the font used for the toc (default Arial)
  • --toc-header-font-name* <name>: The font of the toc header (if unset use --toc-font-name)
  • --toc-header-font-size* <size>: The font size of the toc header (default 15)
  • --toc-header-text* <text>: The header text of the toc (default Table Of Contents)
  • --toc-l1-font-size* <size>: Set the font size on level 1 of the toc (default 12)
  • --toc-l1-indentation* <num>: Set indentation on level 1 of the toc (default 0)
  • --toc-l2-font-size* <size>: Set the font size on level 2 of the toc (default 10)
  • --toc-l2-indentation* <num>: Set indentation on level 2 of the toc (default 20)
  • --toc-l3-font-size* <size>: Set the font size on level 3 of the toc (default 8)
  • --toc-l3-indentation* <num>: Set indentation on level 3 of the toc (default 40)
  • --toc-l4-font-size* <size>: Set the font size on level 4 of the toc (default 6)
  • --toc-l4-indentation* <num>: Set indentation on level 4 of the toc (default 60)
  • --toc-l5-font-size* <size>: Set the font size on level 5 of the toc (default 4)
  • --toc-l5-indentation* <num>: Set indentation on level 5 of the toc (default 80)
  • --toc-l6-font-size* <size>: Set the font size on level 6 of the toc (default 2)
  • --toc-l6-indentation* <num>: Set indentation on level 6 of the toc (default 100)
  • --toc-l7-font-size* <size>: Set the font size on level 7 of the toc (default 0)
  • --toc-l7-indentation* <num>: Set indentation on level 7 of the toc (default 120)
  • --toc-no-dots: Do not use dots, in the toc
    輪廓選項
  • -dump-outline <file>: 轉儲目錄到一個文件
  • --outline: 顯示目錄(文章中h1,h2來定)
  • --outline-depth <level>: 設置目錄的深度(默認爲4)
    頁腳和頁眉
  • [page]: 由當前正在打印的頁的數目代替
  • [frompage]: 由要打印的第一頁的數量取代
  • [topage]: 由最後一頁要打印的數量取代
  • [webpage]: 通過正在打印的頁面的URL替換
  • [section]: 由當前節的名稱替換
  • [subsection]: 由當前小節的名稱替換
  • [date]: 由當前日期系統的本地格式取代
  • [time]: 由當前時間,系統的本地格式取代

頁腳和頁眉

  • [page]: 由當前正在打印的頁的數目代替
  • [frompage]: 由要打印的第一頁的數量取代
  • [topage]: 由最後一頁要打印的數量取代
  • [webpage]: 通過正在打印的頁面的URL替換
  • [section]: 由當前節的名稱替換
  • [subsection]: 由當前小節的名稱替換
  • [date]: 由當前日期系統的本地格式取代
  • [time]: 由當前時間,系統的本地格式取代

源碼分享

/**
 * @Author: WanG
 * @Date: 2019-08-22 13:54
 * @version: v1.0
 * @description: 使用wkhtmltopdf, 來將頁面轉成pdf
 */
@Slf4j
@Service
public class PDFService {
	
	private Random random = new Random();

	/**
	 * 將傳入的頁面轉pdf, 返回字節數組
	 * 默認自動隨機生成文件名字
	 * @author wangq 2019-08-22 16:27
	 * @param url 頁面url
	 * @return
	 */
	public byte[] html2pdf(String url) {
		String tmp = System.getProperty("java.io.tmpdir");
		String filename = System.currentTimeMillis() + random.nextInt(1000) + "";
		return doHtml2pdf(url, tmp + "/" + filename);
	}


	private byte[] doHtml2pdf(String src, String dest) {
		String space = " ";
		String wkhtmltopdf = findExecutable();

		if (StringUtils.isEmpty(wkhtmltopdf)) {
			log.error("no wkhtmltopdf found!");
			throw new RuntimeException("html轉換pdf出錯了");
		}

		File file = new File(dest);
		File parent = file.getParentFile();
		if (!parent.exists()) {
			boolean dirsCreation = parent.mkdirs();
			log.info("create dir for new file,{}", dirsCreation);
		}

		/*
			一些特殊符號
			[page] : 頁碼
			[data] : 日期(例如:8/22/19)
		 */
		StringBuilder cmd = new StringBuilder();
		cmd.append(findExecutable()).append(space)
				.append(src).append(space)
				.append("--footer-center").append(space).append("[page]").append(space)
				.append("--footer-font-size").append(space).append("14").append(space)
				.append("--disable-smart-shrinking").append(space)
				.append("--load-media-error-handling")
				.append(space).append("ignore").append(space)
				.append("--load-error-handling").append(space).append("ignore").append(space)
				.append("--footer-right").append(space).append("WanG提供技術支持").append(space)
				.append(dest);
		InputStream is = null;

		try {
			String finalCmd = cmd.toString();
			log.info("final cmd:{}", finalCmd);

			Process proc = Runtime.getRuntime().exec(finalCmd);

			new Thread(new ProcessStreamHandler(proc.getInputStream())).start();
			new Thread(new ProcessStreamHandler(proc.getErrorStream())).start();

			proc.waitFor();
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			is = new FileInputStream(file);
			byte[] buf = new byte[1024];

			while (is.read(buf, 0, buf.length) != -1) {
				baos.write(buf, 0, buf.length);
			}

			return baos.toByteArray();

		} catch (Exception e) {
			log.error("html轉換pdf出錯", e);
			throw new RuntimeException("html轉換pdf出錯了");

		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	/**
	 * 根據當前系統返回 wkhtmltopdf 執行命令
	 * @author wangq 2019-08-22 16:26
	 * @return
	 */
	public String findExecutable() {
		Process p;
		try {
			String osname = System.getProperty("os.name").toLowerCase();
			String cmd = osname.contains("windows") ? "where wkhtmltopdf" : "which wkhtmltopdf";
			p = Runtime.getRuntime().exec(cmd);
			new Thread(new ProcessStreamHandler(p.getErrorStream())).start();
			p.waitFor();
			return IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
		} catch (Exception e) {
			log.error("no wkhtmltopdf found!", e);
		}
		return "";
	}

	private static class ProcessStreamHandler implements Runnable {
		private InputStream is;
		public ProcessStreamHandler(InputStream is) {
			this.is = is;
		}
		@Override
		public void run() {
			BufferedReader reader = null;
			try {
				InputStreamReader isr = new InputStreamReader(is, "utf-8");
				reader = new BufferedReader(isr);
				String line;
				while ((line = reader.readLine()) != null) {
					log.debug(line);
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (reader != null) {
					try {
						reader.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章