Html to PDF

更新 : 2019-06-27 css

後端製做好 pdf 後,下一個工做就是讓前端下載或者 print 了. html

下載能夠用 <a href="123.pdf" download="abc.pdf" > 的方式前端

print 的話, 能夠用 iframehtml5

const a = document.createElement('a');
a.href = '123.pdf';
a.download = 'abc.pdf';
a.click();

const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.src = 'abc.pdf';
iframe.onload = () => {
  iframe.focus();
  iframe.contentWindow!.print();
};

比較常遇到的問題是跨域, node

iframe 不支持 allow origin 因此咱們不能夠像處理 ajax 那樣去跨域webpack

<a> 不會出現 error 可是不會下載只會開多一個 new tabgit

一個通用的方法是先用 ajax + allow origin 去把 pdf 拿下來. 獲取到 Blob github

而後用 createObjectUrl 來實現web

const result = await this.httpClient.get('http://192.168.1.152:61547/123.pdf', {
  observe: 'response',
  responseType: 'blob'
}).toPromise();

const url = window.URL.createObjectURL(result.body);
const a = document.createElement('a');
a.href = url;
a.download = '123.pdf';
a.click();

const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.src = url;
iframe.onload = () => {
  iframe.focus();
  iframe.contentWindow!.print();
};

這樣就是本地資源了. ajax

針對 <a> 還有一個方法就是不要使用靜態資源路徑, 你改爲好比... /api/download-file 而後後端返回 file 是能夠下載的哦. 

refer : 

https://stackoverflow.com/questions/28318017/html5-download-attribute-not-working-when-downloading-from-another-server-even

https://github.com/crabbly/Print.js/issues/95

http://www.javashuo.com/article/p-zmslogqt-hb.html

 

 

作企業項目,常常會須要保存或輸出 pdf 格式。

好比 invoice, purchase order 等等。

pdf 是 90 年 adobe 公司設計的, 08 年開放. 

製做 pdf 不難,可是互聯網時代,大多數文本都用 html 格式來寫. 

要弄一份如出一轍的 pdf 出來就 double work 了。

因而就有了 html to pdf 的需求。

市場上已經有好多公司作這個出來賣錢了。

好比: 

https://www.nrecosite.com/pdf_generator_net.aspx#

https://www.paperplane.app/pricing

價格都不便宜呢.

html 是 93 年問世的, 很長一段時間, 遊覽器解析引擎都是閉源的. 因此要作 html to pdf 是蠻累的,要解析 html css。

直到 04 年蘋果開源了 safari 的引擎 webkit。 

就有人基於 webkit 作出了 webkit html to pdf

https://wkhtmltopdf.org/

只要安裝 webkit 而後經過 commond 調用一下就能夠把 html 變成 pdf 文件了,開源的哦。

.net https://github.com/rdvojmoc/DinkToPdf 就是基於 wkhtmltopdf 的封裝

有了前車可鑑, 後來的 phantomjs 也有 html to pdf 的功能

jsreport 早年就是基於 phantomjs  實現的 to html  pdf 功能 

https://github.com/jsreport/jsreport 

https://github.com/jsreport/jsreport-dotnet (.net 版本)

一直到 2017 年, chromium 推出了 headless 版. 

chromium 是 google 基於 webkit 的 folk, 如今 chrome 用着的是 blink, chromuim 算是前身吧. 

這個 chromium headless 可厲害了, google 出品, phantomjs 做者隨後就宣佈不在維護了,讓位給 chromium.

chromium 天然是能夠實現 html to pdf 的了. jsreport 後來的版本也切換到 chromium 了. 

chromium 的底層接口不太友好,google 本身又推出了 Puppeteer 

這是 js 的庫, 能夠用 nodejs 運行, 它是 chromium 友好的 api. 幾行代碼就能夠實現各作遊覽器行爲. 固然就包括了 html to pdf。

.net core 和 nodejs 是好朋友, 要在 core 調用 nodejs 的 package 是很簡單的. 

因此又有了 https://github.com/kblok/puppeteer-sharp

我就是使用 asp.net core node service + Puppeteer  來實現 html to pdf 的.

 

上面說的都是 server side 的作法

client side 也有一個作法.

用的是一個叫 jsPDF 的插件, 它是作 pdf 的插件, 而不是 html to pdf 

實現手法是經過打開 browser 使用 url data:application/pdf 的方式輸出 base64 

遊覽器支持這種 data 直接打開 file 的功能,不僅 pdf, image 也是能夠這樣打開.

若是要 html to pdf 能夠配上一個 html to canvas 的插件 

把 html  轉成 canvas 而後變成圖片在輸入進 pdf  ( 固然這樣文字就變成不能夠 search 了 )

目前這個方案問題仍是比較多的, 好比 htmltocanvas 依然處於不穩定版本,並且維護也很弱. 

 

 

記入一下 Puppeteer  的一些東西

module.exports = function (callback, bodyTemplate, format, headerTemplate, footerTemplate) {
    const puppeteer = require('puppeteer');
    (async () => {
        const browser = await puppeteer.launch();
        const page = await browser.newPage();

        //await page.goto('https://www.stooges.com.my');
        //await page.screenshot({ path: 'example.png' });
        //await page.goto('https://www.stooges.com.my', { waitUntil: 'networkidle2' });

        await page.setContent(htmlTemplate, { waitUntil: 'networkidle2' });
        await page.pdf({
            //printBackground: true,
            path: 'demo.pdf',
            format: format, //format or follow width 792 & height 1121, got footer gap... bug 
            landscape: (format === 'A5') ? true : false,
            margin: { top: 125, right: 16, bottom: 60, left: 32 },// 左右會縮小比例, 上下不會; 若是寬沒有hit 到paper的寬就不會縮小; top bottom min 21 for display element
            displayHeaderFooter: true,
            headerTemplate: headerTemplate, //header no backgroung color
            footerTemplate: footerTemplate
        });
        await browser.close();
        //console.log(process.version); // check node version 
        callback(null);
    })();
}

流程大概就是開一個 browser, 開一個 page, 而後訪問你要的網址或者直接寫入 html 

而後調用 pdf 方法. 

當你設定 format A4 時, 你的 width height 就自動設定了, 因該是 792x1121px

header 的開始時 margin 21, 也就是說若是你的 header 內容時 50, 那麼你得要 set margin 71 才能夠哦

還有一個比較嚴重的是 header 和 footer 的字體和 width 都會比 body 大

好比一個 div 在 header 和 body 都  set 500px, 可是出來的效果就是 header 比較大 

大多少呢? 大概是 4/3 . github 還有 issues 討論這個鬼呢. 

 

最後說一說 node_modules 的事兒.

我也是作前端的, 開發前端多少會接觸 node_modules 

這裏說說,前端和後端的小區別. 

1. 後端不須要打包

前端依賴的庫也是存放在 node_modules 裏的, 而後經過 webpack 去打包出來

作後端就不須要去打包了,打包的目的是爲了減小下載, 在服務端 require 另外一個模塊是很快的. 

2. node_modules 須要在 server side 

前端打包之後,咱們的服務器是不須要存在 node_modules 這個文檔的,可是若是是 nodejs 就須要 

咱們對 node_modules 的印象是...很大很大,不少文件. 

但其實不少是 dev 才須要的, 因此在 server side npm install 的時候記得寫上 --production

只使用 puppeteer 的話,大概 3x 個 files 而已.

3. puppeteer 自帶的 chromium 

安裝 puppeteer 時它會去安裝一個 chromium, 放在 node module 裏. 

若是有多個項目,你能夠作一個 share 的 chromium 

調用的時候放一個 path 就能夠了, 咱們能夠經過 Environment Variables 來設置這些 (好比要不要 skip download chromium 和 chromium 的路徑等等...)

const browser = await puppeteer.launch({
    executablePath: 'C:/keatkeat/my projects/asp.net core/2.2/html-to-pdf/Project/.local-chromium/win64-669486/chrome-win/chrome.exe'
});

 

 

 

參考資源 : 

https://wkhtmltopdf.org/

https://www.nrecosite.com/pdf_generator_net.aspx#

https://github.com/wkhtmltopdf/wkhtmltopdf

https://github.com/rdvojmoc/DinkToPdf

https://github.com/jsreport/jsreport

https://github.com/jsreport/jsreport-dotnet

https://github.com/kblok/puppeteer-sharp

https://www.paperplane.app/blog/modern-html-to-pdf-conversion-2019?utm_source=medium&utm_campaign=redirect

https://www.paperplane.app/pricing

https://juejin.im/entry/5ac1e7c05188257ddb0fc853

https://blog.risingstack.com/pdf-from-html-node-js-puppeteer/

https://zhaoqize.github.io/puppeteer-api-zh_CN/#/

https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagepdfoptions

http://www.rotisedapsales.com/snr/cloud2/website/jsPDF-master/docs/jsPDF.html

https://github.com/niklasvh/html2canvas

https://dev.to/damcosset/generate-a-pdf-from-html-and-back-on-the-front-5ff5

https://developers.google.com/web/updates/2017/04/headless-chrome

https://zhuanlan.zhihu.com/p/33015883

https://www.jianshu.com/p/db1b230e3415

https://stackoverflow.com/questions/564650/convert-html-to-pdf-in-net

https://www.quora.com/What-is-the-best-HTML-to-PDF-converter-tool

https://www.reddit.com/r/dotnet/comments/7i6ljt/what_is_currently_the_best_way_to_convert_html_to/

相關文章
相關標籤/搜索