1. 程式人生 > >js根據dom生成pdf

js根據dom生成pdf

背景

  昨天產品經理找我,說現在下載簡歷的功能,下載到的簡歷和ui的不一樣,希望做成一樣的。我的第一反應就是說那我們給後端寫個靜態頁面,後端把相應的欄位套進去吧。產品又說,每個渠道的簡歷的樣式都不一樣,不節能每個渠道都有對應的一個簡歷的靜態頁面,這樣太複雜。希望通過前端下載,即前端直接儲存簡歷預覽頁到本地。其實,一開始我是拒絕的,但是最後想想,寫這麼多靜態頁面也麻煩,索性就嘗試嘗試來解決這個問題吧。

初定技術方案

  拿到這個需求之後,在網上查找了相關的資料。初步有兩個方案。

  • 通過 html2canvas 生成圖片,然後再利用 jsPDF 將圖片匯入pdf並儲存。
  • 通過 html2canvas
    生成圖片,使用 FileSaver 將圖片匯入儲存檔案 。

  對比了一下這兩種技術方案,應該都能滿足使用。

  並且從瀏覽器相容性的角度去看,方案一使用到ArrayBuffer這個類,方案二使用到Bolb類,都只能相容到ie10。然後由於我們這個的需求為儲存PDF檔案,所以我就選了 jsPDF 這個庫作為該方案的解決方案,感覺它專門針對pdf的,顯得更專業點。

小試牛刀

  確定了技術方案,那就按照定好的技術方案來執行,根據官方給的demo先寫一段程式碼實現一下基礎功能,看效果如何,程式碼如下:

import html2canvas from "html2canvas";
import * as jsPDF from "jspdf";
html2canvas(dom).then(canvas => {
     var imgData = canvas.toDataURL('image/jpeg');
     var doc = new jsPDF("p", "mm", "a4");
     doc.addImage(imgData, 'JPEG', 0, 0,210,297);
     doc.save(pdfName + ".pdf");
 })

  這段程式碼很簡單,相信大家也都能看懂。試了一下,確實能生成對應名稱的檔名。開啟檔案一看,感覺有點模糊。。。客戶下載簡歷是需要投遞給hr的,那可不能模糊啊。這個問題需要解決,一定要解決的!!!

解決pdf模糊的問題

  上文中也提到了,程式碼很簡單,模糊無非就兩種情況嘛,一種canvas到的圖片模糊,另外一種是儲存pdf的時候模糊了。那我們就一步步試驗排除一下吧。然後把圖片的 DOMString 列印一下,放到瀏覽器看一下,發現圖片有點模糊,那麼問題定位到了,就想辦法解決吧。

   首先,我想到了 canvas.toDataURL 的api有一個預設圖片質量的引數,如果不填,則是0.92。之前寫過一篇壓縮上傳的圖片就是通過這個引數壓縮的。那就把它改成1吧。該api的詳細文件,參見

文件 。再到瀏覽器裡看看圖片,發現有點效果,但是還是模糊。當然,這個也可能是我的肉眼的問題。還得繼續解決啊。

  然後在官方github上找到了一個關於清晰度的pr,連線如下: 我是修改清晰度pr , 增加了scale和dpi引數,可以設定畫布的dpi,canvas預設的dpi是96dpi,scale預設為1。當兩者同時存在時,以scale為主,會忽略dpi。

  清晰度的問題解決了,那麼是不是大功告成了呢?開始我也這樣認為的,並且也測試著下載了幾個簡歷,完美!但是,我發現當簡歷資料內容很多的時候,下載的pdf字型被壓扁了。我簡單了分析了一下,懷疑是圖片太高,插入一頁pdf,如果圖片的高度大於pdf的高度,是不是會強行的把圖片壓縮成pdf的高度呢?那怎麼辦呢?我們可不可以給pdf做個分頁呢?

分頁解決字型壓扁問題

  既然想到了用分頁,那麼就搜尋了一下jsPDF分頁的內容。發現它有一個 addPage 的函式,那就簡單了。我們可以計算出canvas的高度,已知a4紙的高度,如果大於,就需要分頁,使用addPage函式。這裡也是有兩種解決方案,當然,這兩個方案都不是我自己寫的,哈哈哈。

  方案一

  該方案使用的關鍵技術就是canvas的 drawImage api,把一個長圖按照pdf的高度截取出好多個短圖,然後插入對應的頁面。這個原作者寫的部落格,使用到了遞迴。原部落格 ,作者已經把程式碼帖進去了,我看了一下,然後使用了一下發現是可以用的。

  方案二

  方案二使用的關鍵技術是jsPDF的 addImage 的頁面偏移的位置和jsPD超出內容不顯示的特性。其實理解起來也很簡單,第一頁我插入canvas的偏移位置為0,第二頁偏移位置就是一個a4紙的高度,第三頁就是兩個a4紙的高度,以此類推。原部落格 ,作者已經把程式碼帖進去了,我看了一下,然後使用了一下發現是可以用的。

  當時我還想,同時插入這麼多canvas會不會使pdf檔案的體積變大,後來看了看,測試下來,發現並不會,那個人推薦第二種方案,因為程式碼少,好理解。看方案一的程式碼,也看了一會兒才看明白,哈哈。

   好了,寫到這裡,基本上使用javascript把html轉成pdf下載就寫完了,最後還是要感謝上文中提到的連線。 

  本人部落格地址, 最近搞了一個非常簡單的工時管理小程式,大佬們可以提提建議。