国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【PDF】前端生成pdf

這篇具有很好參考價值的文章主要介紹了【PDF】前端生成pdf。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1、簡要描述

上一篇博客主要講的是pdf文件轉(zhuǎn)換成canvas,然后進行相關的畫框截圖操作。

【PDF】Canvas繪制PDF及截圖

本篇博客主要講html中dom如何生成pdf文件(前端生成pdf),后端生成pdf當然也可以,原理也是將html網(wǎng)頁通過后端服務導出成pdf,然后css設置break-after:always;作為分頁邏輯,但是我們不深入講,這里著重講前端生成pdf。

2、相關插件及知識

還是使用的老朋友jspdf插件和html2canvas

1、jspdf

"jspdf": "^2.5.1"

?使用方法:

import JsPDF from 'jspdf';

const PDF = new jsPDF({
  unit: "mm", // 單位,本示例為mm
  format: "a4", // 頁面大小
  orientation: "portrait", // 頁面方向,portrait: 縱向,landscape: 橫向
  putOnlyUsedFonts: true, // 只包含使用的字體
  compress: true, // 壓縮文檔
  precision: 16, // 浮點數(shù)的精度
});

// 或者

const PDF = new JsPDF('p', 'mm', [210, 297]);

<!-- 常用方法 -->
// 添加圖片
PDF.addImage(
  imageData, // 此值可以為下面這些類型 string | HTMLImageElement | HTMLCanvasElement | Uint8Array | RGBAData
  'JPEG', // 轉(zhuǎn)換后的格式
  x, // 被切割的imageData的橫坐標
  y, // 被切割的imageData的縱坐標
  w, // 當前圖片的寬度
  h, // 當前圖片的高度
);
// 添加新的一頁
PDF.addPage();
// 輸出格式
PDF.output(type: "arraybuffer"): ArrayBuffer;
PDF.output(type: "blob"): Blob;
PDF.output(type: "bloburi" | "bloburl"): URL;
// 本地保存為pdf文件
PDF.save('lindadayo.pdf')

2、html2canvas

"html2canvas": "^1.4.1"
// 實例方法   
html2canvas(dom, config).then(function(canvas) {})

?config相關配置參考下圖:前端生成pdf,PDF,pdf

3、源碼

1、dom結(jié)構

前端生成pdf,PDF,pdf

2、核心邏輯

這里為什么要將dom進行分區(qū)處理呢?請看第四點疑難解答中1、為什么要對dom進行分區(qū)操作?

    /**
     * 生成pdf
     * @param CommonPage 需要轉(zhuǎn)換的dom節(jié)點
     * @param i 分區(qū)索引
     * @returns
     */
    async generatePdf(CommonPage?: Element, childLen?: number) {
      PDF = new JsPDF('p', 'mm', [210, 297]); // pdf實例
      for (let i = 0; i < childLen; i++) {
        await asyncSingleAreaControl(CommonPage, i)
      }
      generateUploadPdf();
    },
    /**
     * 上傳pdf文件
     */
    async generateUploadPdf() {
      // 文件重命名,修改生成pdf后的文件名
      const pdfName = pdfNameHandle()
      const uri = PDF.output('blob')
      const file = await blobUriToFile(uri, pdfName)
      // 此時的file是File類對象,你可以選擇上傳到服務器噢~當然你也可以選擇直接導出到前端
      // PDF.output('lindadayo.pdf');
    },
    /**
     * 單個分區(qū)生成pdf操作
     * @param CommonPage 父節(jié)點dom
     * @param i 分區(qū)索引
     * @returns
     */
    async asyncSingleAreaControl(CommonPage, i) {
      const canvas = await singleHandle(CommonPage, i)
      await areaPage(canvas, i)
    },
    /**
     * 分區(qū)pdf處理
     * @param canvas 各個分區(qū)dom轉(zhuǎn)換后的canvas
     * @param areaNo 分區(qū)索引
     */
    areaPage(canvas, areaNo) {
      // 是否是第一個分區(qū)(作用于是否開始就addPage)
      const isFirstArea = areaNo === 0
      return new Promise((resolve, _reject) => {
        // a4紙寬高
        const A4Origin = {
          width: PDF.internal.pageSize.getWidth(),
          height: PDF.internal.pageSize.getHeight()
        }
        const contentWidth = canvas.width;
        /**
         * html2canvas放大3.125倍時精度丟失導致多了2像素
         * 3368: 高度285mm紙張html2canvas放大300dpi后像素
         * 3366:正常實際高度
         */
        const contentHeight = canvas.height <= 3368 ? 3366 : canvas.height;
        const pageHeight = Math.round(contentWidth / A4Origin.width * A4Origin.height);
        let leftHeight = contentHeight;
        let position = 0;
        const imgWidth = A4Origin.width;
        const imgHeight = Math.ceil(A4Origin.width / contentWidth * contentHeight);
        const pageData = canvas.toDataURL('image/jpeg', 1);
        // 非首個分區(qū),得先addPage,因為不然會少一頁 && 大于某個范圍才新增一頁,避免因為浮點數(shù)計算精度造成多增一頁
        if (!isFirstArea && leftHeight > 0) {
          PDF.addPage()
        }
        while (leftHeight > 0) {
          PDF.addImage(pageData, 'JPEG', 0, position, imgWidth + (isBrower() ? 0.62 : 0), imgHeight + (isBrower() ? 0.32 : 0));
          position -= A4Origin.height;
          leftHeight -= pageHeight
          // 大于某個范圍才新增一頁,避免因為浮點數(shù)計算精度造成多增一頁
          if (leftHeight > 0) {
            PDF.addPage()
          }
        }
        resolve(true)
      })
    },
    /**
     * 單頁pdf處理
    //  * @param root 總節(jié)點
     * @param index 分區(qū)索引
     */
    async singleHandle(CommonPage, index) {
      // 報錯Unable to find element in cloned iframe解決方法
      // getDiv在外部聲明, 內(nèi)部賦值
      try {
        getDiv = CommonPage.querySelector(`#CommonPageItemArea-${index}`)
        const res = await html2canvas(getDiv, {
          useCORS: true,
          allowTaint: true,
          scale: 3.125
        }).then(function(canvas) {
          return canvas
        })
        return res
      } catch (e) {
        console.log(e)
      }
    }

4、疑難解答

1、為什么要對dom進行分區(qū)操作?

其實如果你不使用html2canvas的參數(shù)scale,就沒必要進行分區(qū),但是在很多時候,你不放大canvas的話,會導致pdf中的圖片很模糊,還有鋸齒,所以要對canvas進行方法,但是放大后,會導致一些問題:生成pdf后,超過15000px以后的dom會有樣式丟失,所以得對dom進行分區(qū)操作,讓每個分區(qū)的dom高度 * 放大倍數(shù)不超過15000px。我們一般都會導出a4紙大小,a4紙寬高是210mm*297mm,換算成像素是793.29px * 1122.52px,如果你選擇放大兩倍,那么,單頁高度就是2245px,結(jié)論為一個分區(qū)能夠放六個a4紙高度的dom,所以你在開發(fā)頁面時,就要做好這種頁面結(jié)構噢~

2、html2canvas仍然報圖片出錯/跨域的問題,即使后端oss已經(jīng)解決跨域了

報錯Error loading image

前端生成pdf,PDF,pdf

這個涉及知識點:img標簽實例化獲取屬于非跨域操作,Image類實例化屬于跨域操作,所以得再html2canvas依賴中打補丁,當圖片是你本地的靜態(tài)圖片,那不需要轉(zhuǎn),還是按照Image實例化來做,當圖片已經(jīng)是base64格式的話,也不需要轉(zhuǎn),賦值給img標簽,否則的話加上隨機數(shù)。

/dist/html2canvas.js 第5759行

前端生成pdf,PDF,pdf

3、報錯Unable to find element in cloned iframe解決方法

在分區(qū)中循環(huán)處理dom生成canvas時會報出這種錯誤,原因是html2canvas第一參數(shù)的變量應該設置為全局變量而不應該是局部變量

    try {
        getDiv = CommonPage.querySelector(`#CommonPageItemArea-${index}`)
        const res = await html2canvas(getDiv, {
          useCORS: true,
          allowTaint: true,
          scale: 3.125
        }).then(function(canvas) {
          return canvas
        })
        return res
      } catch (e) {
        console.log(e)
      }

4、dom-to-image和html2canvas相比,哪個更優(yōu)?

dom-to-image是一個js庫,可以將任意dom節(jié)點轉(zhuǎn)換為矢量(SVG)或光柵(PNG或JPEG)圖像。和html2canvas相比的話,算是一個新起之秀,更輕巧,相同點就是都會先將dom轉(zhuǎn)成canvas進行操作,所以在dom層級深和多的情況下,還是建議使用html2canvas這種老牌插件

5、生成pdf里圖片缺失

那是因為圖片轉(zhuǎn)換及獲取是異步的,需要時間渲染,所以生成pdf的步驟應該在圖片完全加載完之后,由此我們可以加個定時器來循環(huán)判斷全部圖片是否加載完成,加載完成再進行生成操

    /**
     * CommonPage生成dom渲染完成
     * @param callback
     */
    commonPageLoadFinish(callback) {
      nextTick(() => {
        // 生成節(jié)點
        const CommonPage = document.querySelector('#CommonPage')
        const childLen = CommonPage.querySelectorAll('.CommonPageItemArea').length
        console.log('分區(qū)數(shù)量', childLen)
        if (childLen > 0) {
          let timer = null;
          // 監(jiān)聽頁面中所有轉(zhuǎn)base64圖片是否已生成完畢,如果已生成完畢,則進入下一步與dom相關的操作
          timer = setInterval(() => {
            // isImageAllCompleted.sum => 圖片總數(shù)量, isImageAllCompleted.loadSum => 目前圖片已經(jīng)加載完的數(shù)量
            if (isImageAllCompleted.sum === isImageAllCompleted.loadSum) {
              clearInterval(timer)
              timer = null
              callback(CommonPage, childLen)
            }
          }, 1000)
        }
      })
    }

將生成pdf步驟作為回調(diào)函數(shù)放在上述函數(shù)里

commonPageLoadFinish(generatePdf)

?那在組件中如何監(jiān)聽圖片是否加載完成呢?按照以下代碼來寫

    onMounted(() => {
      nextTick(() => {
        // commonRef.value 為某dom的refs
        // 被動檢測是否有圖片, 無則直接進入主邏輯
        const img = commonRef.value.querySelectorAll('img');
        if (!img.length) return methods.successCallback();
        let imgSum = 0;
        asyncImgCompLoad(img).then((res) => {
          res.forEach(() => imgSum++);
          // 設置圖片總數(shù)量和加載數(shù)量
          setImageAllCompleted({ sum: isImageAllCompleted.sum + img.length, loadSum: imgSum + isImageAllCompleted.loadSum });
        })
      })
    })


    async asyncImgCompLoad(imgList) {
        const promiseList = []
        for await (const item of imgList) {
          promiseList.push(new Promise((res, rej) => {
            // 參數(shù)沒有被賦值
            if (!item.src) {
              rej(false)
            }
            if (item.complate) {
              res(true)
            } else {
              item.addEventListener('load', () => {
                res(true)
              })
              // 圖片被賦值,但是賦的是錯誤的值
              item.addEventListener('error', () => {
                rej(false)
              })
            }
          }))
        }
        return Promise.allSettled(promiseList)
      }

?6、生成的pdf里,單頁底部有白邊?

在使用PDFjs插件時候,加入需要導出a4紙大小,那么很多童鞋就會將寬高固定設置為210mm, 297mm,但是實際上不是整數(shù),是小數(shù),所以獲取時按照下述方法獲取

        // a4紙寬高
        const A4Origin = {
          width: PDF.internal.pageSize.getWidth(),
          height: PDF.internal.pageSize.getHeight()
        }

PDF分頁核心源碼

        // a4紙寬高
        const A4Origin = {
          width: PDF.internal.pageSize.getWidth(),
          height: PDF.internal.pageSize.getHeight()
        }
        const contentWidth = canvas.width;
        /**
         * html2canvas放大3.125倍時精度丟失導致多了2像素
         * 3368: 高度285mm紙張html2canvas放大300dpi后像素
         * 3366:正常實際高度
         */
        const contentHeight = canvas.height <= 3368 ? 3366 : canvas.height;
        const pageHeight = Math.round(contentWidth / A4Origin.width * A4Origin.height);
        let leftHeight = contentHeight;
        let position = 0;
        const imgWidth = A4Origin.width;
        const imgHeight = Math.ceil(A4Origin.width / contentWidth * contentHeight);
        const pageData = canvas.toDataURL('image/jpeg', 1);
        // 非首個分區(qū),得先addPage,因為不然會少一頁 && 大于某個范圍才新增一頁,避免因為浮點數(shù)計算精度造成多增一頁
        if (!isFirstArea && leftHeight > 0) {
          PDF.addPage()
        }
        while (leftHeight > 0) {
          PDF.addImage(pageData, 'JPEG', 0, position, imgWidth + (isBrower() ? 0.62 : 0), imgHeight + (isBrower() ? 0.32 : 0));
          position -= A4Origin.height;
          leftHeight -= pageHeight
          // 大于某個范圍才新增一頁,避免因為浮點數(shù)計算精度造成多增一頁
          if (leftHeight > 0) {
            PDF.addPage()
          }
        }

?有兩個地方可能童鞋們沒看懂,1、首先為啥非首個分區(qū),得先addPage呢?因為PDF默認就有一頁,所以你能夠直接addImage而不出錯,然后后續(xù)PDF想要新增一頁,都得先addPage,這時候默認背景顏色是白色的,然后再將canvas轉(zhuǎn)成圖片,貼到這白板上的,所以你看到PDF文檔里有白邊,那毫無疑問,就是貼的圖片沒占完那一頁,并且火狐瀏覽器和谷歌瀏覽器還有一些細微的差別所以你這就得一點一點微調(diào)來達到最佳顯示效果。2、為啥addIMage時,里面?zhèn)鞯膮?shù)不同呢?

PDF.addImage(pageData, 'JPEG', 0, position, imgWidth + (isBrower() ? 0.62 : 0), imgHeight + (isBrower() ? 0.32 : 0));

?這就是瀏覽器差異問題,火狐瀏覽器不僅底部有白邊,側(cè)面也有白邊,相比之下谷歌要更兼容一些。

?7、pdf生成的File文件對象,想要先傳入oss,再通過服務端下載怎么實現(xiàn)?

這其實就涉及到大文件上傳技術了,因為pdf稍微大點可能都上百M,一般都不會一次性上傳完的,所以得做切片上傳,然后在服務端合并上傳到oss,最后將oss路徑地址返回給前端,前端通過這地址去下載。當然具體的大文件上傳我就不寫在這篇博客了,下一篇博客我將著重講大文件上傳如何寫噢~

8、vue-fragment插件配合html2canvas使用有問題?

在@vue/composition-api環(huán)境下開發(fā),想使用fragment就得使用vue-fragment插件,但是搭配上html2canvas導出canvas會出現(xiàn)bug,就是只會渲染第一個fragment標簽的dom,其他的都不會渲染,目前還是建議在@vue/composition-api環(huán)境下不使用fragment

--- 有問題可以隨時評論噢~喜歡的請點贊收藏啦 ---文章來源地址http://www.zghlxwxcb.cn/news/detail-703269.html

到了這里,關于【PDF】前端生成pdf的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 【PDF】html/dom生成pdf

    【PDF】html/dom生成pdf

    上一篇博客主要講的是pdf文件轉(zhuǎn)換成canvas,然后進行相關的畫框截圖操作。 【PDF】Canvas繪制PDF及截圖 本篇博客主要講html中dom如何生成pdf文件(前端生成pdf),后端生成pdf當然也可以,原理也是將html網(wǎng)頁通過后端服務導出成pdf,然后css設置break-after:always;作為分頁邏輯,但是

    2024年02月16日
    瀏覽(26)
  • 【生成PDF】【JAVA】純后臺生成Echarts圖片,并將圖片生成到PDF文檔

    【生成PDF】【JAVA】純后臺生成Echarts圖片,并將圖片生成到PDF文檔

    目錄 前言 一、如何后臺生成Echarts圖片? 1.PhantomJS 2.PhantomJS的下載 ?3.用phantomjs調(diào)用echarts-converts.js生成圖片 二、Java如何將Echarts圖生成到PDF 1.生成PDF依賴 2.Java代碼測試例子: ?3.測試結(jié)果? ?三、下載生成的PDF ReportFormUtil 提示:本文僅用于記錄日常,多有不足,僅供參考。

    2024年02月09日
    瀏覽(32)
  • vue 純前端預覽pdf,純前端實現(xiàn)pdf加水印下載文件也帶水印,防止pdf下載

    vue 純前端預覽pdf,純前端實現(xiàn)pdf加水印下載文件也帶水印,防止pdf下載

    ? 原理:主要是利用pdfh5這個插件來完成的 ? 使用方法: ? 1.頁面需要有一個容器例子:div id=\\\"demo\\\"/div ? 2.下載pdfh5插件 npm install pdfh5 ? (注意:webpack5之后不會下載polyfill 需要手動下載 所以引入pdfh5的時候會報錯) ? 解決方案:下載 node-polyfill-webpack-plugin npm install node-polyfill-

    2024年04月15日
    瀏覽(129)
  • vue前端實現(xiàn)將頁面顯示內(nèi)容生成pdf文件的幾種方法,html2canvas、dom-to-image、jspdf(帶分頁)基本使用以及介紹

    實際開發(fā)需求:vue項目中,根據(jù)數(shù)據(jù)結(jié)構生成echarts圖表組件,生成帶有樣式的圖表以后,點擊下載按鈕,把圖表以pdf格式的文件下載到本地 實現(xiàn)思路:將vue界面的echarts組件生成圖片,然后使用插件將生成的圖片放入pdf中,再實現(xiàn)pdf文件的下載 涉及框架以及插件:vue、echar

    2024年01月25日
    瀏覽(30)
  • Java doc等文件生成PDF、多個PDF合并

    之前寫過一遍文章是 圖片生成PDF。 今天繼續(xù)來對 doc等文件進行pdf合并以及多個pdf合并為一個pdf。 兄弟們,還是開箱即用。 依賴 示例代碼 依賴 示例代碼

    2024年02月10日
    瀏覽(23)
  • java集成itextpdf實現(xiàn)通過pdf模板填充數(shù)據(jù)生成pdf

    java集成itextpdf實現(xiàn)通過pdf模板填充數(shù)據(jù)生成pdf

    我采用的是pdfelement 官網(wǎng)地址需要付費或者自行破解,也可以使用其他pdf編輯器。 將制作好的pdf模板放入項目resources/pdf目錄下,如圖 瀏覽器訪問ip:port/test/pdf,其中ip為你的ip地址,port為你的端口,訪問結(jié)果如下:

    2024年02月16日
    瀏覽(23)
  • 前端導出PDF(純前端功能)

    前端導出PDF(純前端功能)

    例如:需要導出PDF格式 提示:問題出現(xiàn)在滾動條加錯地方 首先: npm install html2canvas npm install jspdf 第二:新建一個htmlToPdf.js文件存放 // 導出頁面為PDF格式 import html2Canvas from \\\'./html2canvas\\\' import JsPDF from \\\'./jsPdf.debug\\\' export default{ ? name:\\\'wwutils\\\', ? toolExportPdf(fileName) { ? ? let isCompele

    2024年02月16日
    瀏覽(20)
  • Springboot -- 按照模板生成docx、pdf文件,docx轉(zhuǎn)pdf格式

    Springboot -- 按照模板生成docx、pdf文件,docx轉(zhuǎn)pdf格式

    使用 poi-tl 根據(jù)模板生成 word 文件。 使用 xdocreport 將 docx 文件轉(zhuǎn)換為 pdf 文件。 xdocreport 也支持根據(jù)模板導出 word ,但是 poi-tl 的功能更齊全,操作更簡單,文檔清晰。 poi-tl 、xdocreport 內(nèi)部均依賴了 poi ,要注意兩者中 poi 和 自身項目引用的 poi 的版本是否存在沖突。 使用 p

    2024年02月15日
    瀏覽(19)
  • 從數(shù)據(jù)處理到3D PDF生成:交互式3D PDF生成引擎HOOPS Publish的工作原理

    從數(shù)據(jù)處理到3D PDF生成:交互式3D PDF生成引擎HOOPS Publish的工作原理

    在當今數(shù)字化時代,3D技術在各個行業(yè)中扮演著重要角色,從制造業(yè)到醫(yī)療保健,再到建筑設計。為了更好地共享、演示和交互展示3D模型數(shù)據(jù),HOOPS Publish作為一款強大的3D引擎,專門用于生成交互式的3D PDF文件。本文將深入探討HOOPS Publish是如何實現(xiàn)生成3D PDF的。 點擊獲取

    2024年03月10日
    瀏覽(31)
  • 前端使用pdf-lib庫實現(xiàn)pdf合并,window.open預覽合并后的pdf

    最近出差開了好多發(fā)票,寫了一個pdf合并網(wǎng)站,用于把多張發(fā)票pdf合并成一張,方便打印 使用pdf-lib這個庫實現(xiàn)的pdf合并功能,預覽使用的是瀏覽器自身查看pdf功能 源碼 網(wǎng)頁地址 https://zqy233.github.io/PDF-merge/

    2024年02月11日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包