轉換 base64 成圖片再轉換成 PDF 的踩坑筆記
需求
最近在實作一個功能,主要是將 Canvas 轉換成圖片後,再與給定圖片合併,最後轉換成 PDF。在這之前找了一些相關可以使用的套件,好在已經有人實作過,讓我減少一些在額外開發其他功能的時間。
在實作過程中,我把該套件再透過 Vue 進行一層包裝,讓我在開發可以把它作為元件(Compononet)四處使用,有興趣可以參考 - vue-signature-pad;基本上這個元件的功能主要都是為了系統功能部分需求而去實作的,但是原先套件的重要的核心功能都有加入進來。
嘗試開發
首先,我透過 vue-signature-pad 提供了一個畫板給使用者,使用者繪製完畢儲存後,我會將這個 Canvas 畫板的內容轉換成圖片並儲存,接著再把它轉成 base64 提供給使用者繪製後的預覽結果,基本上透過 vue-signature-pad 就可以完成這件事情,在 vue-signature-pad README 有提供一個將畫板內容轉換成 base64 的簡單範例:
<template><div id="app"><VueSignaturePadwidth="500px"height="500px"ref="signaturePad"/><div><button @click="save">Save</button><button @click="undo">Undo</button></div></div></template><script>export default {name: 'MySignaturePad',methods: {undo() {this.$refs.signaturePad.undoSignature();},save() {const { isEmpty, data } = this.$refs.signaturePad.saveSignature();console.log(isEmpty);console.log(data);}}}</script>
接下來會把 base64 的資料送到後端,把他轉換後儲存成圖片,一開始是使用 Intervention/image 這個 library 來處理,因為剛好系統就有使用到這個套件,處理的方式為:
$base64Image = $request->input('b64Img');$image = Image::make($base64Image)->stream('png');Storage::disk('local')->put('your_specified_path'.'/sampleImage.png', $image);
將傳入的 base64 透過 Image::make()
再轉成 stream,接著透過 Store
將 $image
內容放入儲存到指定的位置,在操作以上過程後,圖片可以成功的被儲存。
問題
本來以為把圖片儲存後,在與指定的圖片做合併轉換成 PDF 檔案應該就沒什麼問題了,於是就按照這個想法繼續往下實作。
關於圖片轉換成 PDF 檔案,我是使用 imagemagick 來處理的,這裡就不多闡述。
在合併轉換 PDF 完成後,開心地打開 PDF 檔案,結果發現:
怎麼會第一頁和第二頁的大小不一致!事先準備好的圖片大小是一樣的,照理來說不應該出現這樣的情況,於是我開始回頭檢查整個流程:
- 檢查 Canvas 轉成 base64 的結果
- 檢查
Image
轉換 base64 成圖片的結果
- 檢查
- 檢查 imagemagick 合併過程
流程檢查完後的結果看起來都沒問題,但是轉出來的 PDF 一直有問題,我一直覺得是在 imagemagick 出了問題,於是我另外重新合併指定圖片成兩頁,結果是正確的!
於是我回頭再去檢查步驟二圖片的尺寸大小,也是跟原先的尺寸一樣,看起來沒問題,可以推理到第一步的結果也是沒問題的,思考了許久後來發覺問題還是可能出在步驟二,畢竟透過了 library 來幫我做了轉換,也不太確定這轉換的中間 library 會不會有其他的處理,於是先把 Image
給拿掉,透過 PHP 內建函式來作轉換:
$base64Image = $request->input('b64Img');$image = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $base64Image));Storage::disk('local')->put('your_specified_path'.'/sampleImage.png', $image);
再重新跑整個流程一次,最後打開 PDF 的結果是 - 正確!
沒想到問題竟然是出在 Image
上,本來以為是因為把它轉換成 stearm()
造成的問題,後來嘗試修改成 Image::make($base64Image)->encode('png')->encoded
結果還是跟原先一樣,不確定 library 內部還有沒有做了其他事情造成這樣的結果,因為開發時間蠻趕的,沒有特別去深入去追這裡的問題,想說 PHP 內建函式可以處理掉也是可以接受的,之後有時間回頭再來找找好了!