在純JaveScript中實現報表匯出:從“PDF”到“JPG”

葡萄城技術團隊發表於2021-11-03

我們在前端報表中完成了各種工作資料的輸入或內容處理之後,需要做什麼?

資料的匯出!

這些資料的常用匯出格式有:PDF、Excel、HTML和圖片幾大型別。

但總有一些實際應用場景,需要的不僅僅是將現有內容匯出,還需要我們對一些內容的格式進行轉化。

就在前幾天,葡萄剛上班,就看到客戶發來下圖,發生了以下對話

-葡萄,這一頁可以匯出嗎?

-當然可以,PDF、Excel、HTML都可以。

-可是我想把這一頁匯出圖片。

這時候問題就出現了,在我們的前端電子報表中並沒有預設圖片儲存的格式,那這時候我們如何用已有功能進一步擴充套件,來實現這個功能呢?

一、確定實現思路

巧婦難為無米之炊,首先我們先整理一下手中素材。

通過閱讀文件瞭解我們可以自定義新增按鈕:

同時我們還可以在action屬性中,給按鈕定義點選後觸發的事件:

順著這個思路,我們可以在工具欄新增一個匯出按鈕,將按鈕的動作設定為"點選這個按鈕時實現匯出圖片的功能"。ARJS本身支援匯出PDF,並且也提供了直接呼叫匯出PDF的介面:export,所以我們可以先通過介面匯出PDF,然後再將PDF轉換為圖片,最終實現匯出圖片的功能。

這下子,我們的最終問題就變成了是如何 PDF 轉換為圖片並匯出

PDF.js是一款使用HTML5 Canvas安全地渲染PDF檔案以及遵從網頁標準的網頁瀏覽器渲染PDF檔案的JavaScript庫。我們可以通過PDF.js庫將匯出的PDF通過Canvas在網頁上渲染出來,然後通過Canvas的toDataURL方法返回一個包含圖片展示的 data URL。拿到這個URL就好辦了,可以利用a標籤的download屬性直接對其進行下載,最終實現在ARJS中匯出圖片的功能。

總結,整體實現思路如下:

  • 新增匯出圖片按鈕
  • 實現匯出PDF
  • 將 PDF 通過 PDF.js 庫渲染成 <canvas>
  • 通過a標籤的download屬性將<canvas>儲存為圖片

二、程式碼實戰
簡單起見,本示例不使用任何框架整合ARJS,選擇在純JaveScript中整合報表,大家可以閱讀相關文件:在純JavaScript專案中整合報表 Viewer。另外,為了在document中插入canvas元素,事先可以建立一個div元素,以便之後在該節點下插入canvas元素;同時為了介面中只有報表檢視器,可以隱藏該div。最終的頁面結構如下:

    <body>
            <div id="viewer-host"></div>
            <div id="imgDiv" style="display: none"></div>
    </body>

新增匯出圖片按鈕

1.        let exportImageButton = {
2.            key: '$exportImage',
3.            icon:{
4.                type: 'svg',
5.                content:'<svg role="img" xmlns="http://www.w3.org/2000/svg" width="21px" height="21px" viewBox="0 0 24 24" aria-labelledby="imageIconTitle" stroke="#205F78" stroke-width="2.2857142857142856" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#205F78"> <title id="imageIconTitle">Image</title> <rect width="18" height="18" x="3" y="3"/> <path stroke-linecap="round" d="M3 14l4-4 11 11"/> <circle cx="13.5" cy="7.5" r="2.5"/> <path stroke-linecap="round" d="M13.5 16.5L21 9"/> </svg>',
6.                size: 'small'
7.            },
8.            enabled: true,
9.            title:'匯出圖片',
10.            action: function() {
11.                //定義匯出圖片按鈕點選事件
12.            }
13.        };
14.        viewer.toolbar.addItem(exportImageButton);

介面文件:addItem
(提示:以上在icon 的content的屬性中,使用了一個svg,這個示例程式碼中的svg來自網站:ikonate 。如果大家有需要可自行下載,如果作為商用需要注意版權 )

以上程式碼新增之後,我們就可以在報表預覽介面的工具欄看到這樣一個按鈕:

實現匯出PDF

在exportImageButton的action中定義一個exportImage方法,在這個方法中首先實現匯出PDF,匯出的結果包含一個PDF檔案的blob物件,大家可自行列印出來看一下匯出結果:

        function exportImage() {
            const settings = { title: 'Active Reports JS' };
            viewer.export('PDF', settings).then((result) =>{
                    //這個result包含一個所匯出PDF的blob物件
                    console.log(result);
            });
        }

將PDF通過PDF.js庫渲染成canvas

首先我們需要去PDF.js官網下載相關檔案引入到專案中,我這裡的示例通過cdn的方式引入:

1.      <script src="https://cdnjs.cloudflare.com/ajax/libs/PDF.js/2.10.377/PDF.min.js"></script>

引入之後,我們就可以對上一步得到的blob物件進行操作,將PDF渲染成\&lt;canvas\&gt;:

        function pageToCanvasObj(page) {
            const viewport = page.getViewport({scale: 1});
            const canvas = document.createElement('canvas');
            const context = canvas.getContext('2d');
            canvas.height = viewport.height;
            canvas.width = viewport.width;
            canvas.style.width = "100%";
            canvas.style.height = "100%";
            imgDiv.append(canvas);
            return {
                canvas,
                renderContext: {
                    canvasContext: context,
                    viewport
                }
            }
        }
    
        function exportImage() {
            const settings = { title: 'Active Reports JS' };
            viewer.export('PDF', settings).then((result) =>{
                //核心程式碼
               //通過FileReader的介面將blob轉換為ArrayBuffer
                const fileReader = new FileReader();
                fileReader.readAsArrayBuffer(result.data);
                fileReader.onload = function() {
                    //為了讀寫ArrayBuffer物件,建立typedArray檢視
                    const typedArrayResult = new Uint8Array(fileReader.result);
                    //PDF.js讀取文件後渲染canvas
                    PDFjsLib.getDocument(typedArrayResult).promise.then(function(PDF) {
                        if (PDF) {
                            const pageNum = PDF.numPages;
                            for (let i = 1; i <= pageNum; i++) {
                                PDF.getPage(i).then((page) => {
                                    //建立canvas,並且返回相關資料
                                    const canvasObj = pageToCanvasObj(page);
                                    //<canvas>渲染
                                    page.render(canvasObj.renderContext).promise.then(() => {
                                       //通過canvas物件的toDataURL得到圖片連結
                                        const imgUrl = canvasObj.canvas.toDataURL();
                                    })
                                })
                            }
                        }
                    },(error) => {
                        alert(error);
                    });
                };
            });
        }

通過a標籤的download屬性將canvas儲存為圖片
將上一步得到的imgURL通過a標籤下載:

   function saveImage(index, url) {
            const link = document.createElement("a");
            link.href = url;
            link.download = `image${index}`;
            link.click();
            link.remove();
        }

這下就實現了在在前端報表中完整將報表內容作為圖片匯出。在此附上示例完整demo程式碼檔案:

https://gcdn.grapecity.com.cn...;aid=MTY0Njg4fGNlMzM5MTkwfDE2MzM2NjU4MzB8NjI2NzZ8MTMyNDM3
匯出效果:

到這裡,已經完全解決了本次提到的問題~

後續也會為大家帶來更多有趣或嚴肅的內容。

相關文章