前端 jsPDf和html2canvans html转化pdf

1.html转化为pdf过程

        html2canvas(element, {
          dpi: 120, // 图片清晰度问题
        }).then(canvas => {
          let contentWidth = canvas.width // 画布的宽度
          let contentHeight = canvas.height // 画布的高度
          let pageHeight = contentWidth / 592.28 * 841.89 // 每一页的高度
          let leftHeight = contentHeight // 偏移的位置
          let position = 0
          let imgWidth = 595.28
          let imgHeight = 592.28 / contentWidth * contentHeight
          let pageData = canvas.toDataURL('image/jpeg', 1.0)
          let PDF = new jsPDF('', 'pt', 'a4')
          PDF.text(100, 100, '设置的表头的参数')
          let pageArr = []
          // if (leftHeight < pageHeight) {
          //   PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
          // } else {
          //   while (leftHeight > 0) {
          //     PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
          //     leftHeight -= pageHeight
          //     position -= 841.89
          //     pageArr.push(position)
          //     console.log('打印的页码数是多少', position)
          //     if (leftHeight > 0) {
          //       PDF.addPage()
          //     }
          //   }
          // }
          PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, 5000)
          console.log('打印页码数', pageArr)
          pageArr.forEach((val,index) => {
            console.log('循环的数组', index)
          })
          PDF.save('api.pdf')
      })

上述过程会导致 内容截断  

代码修改一:整体一页  不分页

      html2canvas(element, {
        allowTaint: true,
        scale: 2 // 提升画面质量,但是会增加文件大小
      }).then(function (canvas) {
        /**jspdf将html转为pdf一页显示不截断,整体思路:
         * 1. 获取DOM 
         * 2. 将DOM转换为canvas
         * 3. 获取canvas的宽度、高度(稍微大一点)
         * 4. 将pdf的宽高设置为canvas的宽高
         * 5. 将canvas转为图片
         * 6. 实例化jspdf,将内容图片放在pdf中(因为内容宽高和pdf宽高一样,就只需要一页,也防止内容截断问题)
         */

        // 得到canvas画布的单位是px 像素单位
        var contentWidth = canvas.width
        var contentHeight = canvas.height

        console.log('contentWidth', contentWidth)
        console.log('contentHeight', contentHeight)
        // 将canvas转为base64图片
        var pageData = canvas.toDataURL('image/jpeg', 1.0)

        // 设置pdf的尺寸,pdf要使用pt单位 已知 1pt/1px = 0.75   pt = (px/scale)* 0.75
        // 2为上面的scale 缩放了2倍
        var pdfX = (contentWidth + 10) / 2 * 0.75
        var pdfY = (contentHeight + 500) / 2 * 0.75 // 500为底部留白

        // 设置内容图片的尺寸,img是pt单位 
        var imgX = pdfX;
        var imgY = (contentHeight / 2 * 0.75); //内容图片这里不需要留白的距离

        // 初始化jspdf 第一个参数方向:默认''时为纵向,第二个参数设置pdf内容图片使用的长度单位为pt,第三个参数为PDF的大小,单位是pt
        var PDF = new jsPDF('', 'pt', [pdfX, pdfY])

        // 将内容图片添加到pdf中,因为内容宽高和pdf宽高一样,就只需要一页,位置就是 0,0
        PDF.addImage(pageData, 'jpeg', 0, 0, imgX, imgY)
        PDF.save('api.pdf')
      })

修改实现2: 未作具体研究实现

<div className='content' id='pdfDom'>
    <div className='item'>内容</div>
    <div className='item'>内容</div>
    <!--    每一块dom的class类设置成item(自定义)以此处理内容分割  -->
    <div className='item'>内容</div>
    <div className='item'>内容</div>
</div>
组件内编写导出方法exportPDF,(其中isSplit方法判断是否要分割)
代码如下:

import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'

//避免分页被截断
const exportPDF = (pdfDom, title) => {
    const A4_WIDTH = 592.28;
    const A4_HEIGHT = 841.89;
    // myLoading 自定义等待动画组件,实现导出事件的异步等待交互
    // dom的id。
    let target = document.getElementById(pdfDom);
    let pageHeight = target.scrollWidth / A4_WIDTH * A4_HEIGHT;
    // 获取分割dom,此处为class类名为item的dom
    let lableListID = target.getElementsByClassName('item');
    // let lableListID = document.getElementsByClassName('item');
    // 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割
    for (let i = 0; i < lableListID.length; i++) {
        let multiple = Math.ceil((lableListID[i].offsetTop + lableListID[i].offsetHeight) / pageHeight);
        if (isSplit(lableListID, i, multiple * pageHeight)) {
            let divParent = lableListID[i].parentNode; // 获取该div的父节点
            let newNode = document.createElement('div');
            newNode.className = 'emptyDiv';
            newNode.style.background = '#01195e';
            let _H = multiple * pageHeight - (lableListID[i].offsetTop + lableListID[i].offsetHeight);
            newNode.style.height = _H + 30 + 'px';
            newNode.style.width = '100%';
            let next = lableListID[i].nextSibling; // 获取div的下一个兄弟节点
            // 判断兄弟节点是否存在
            // console.log(next);
            if (next) {
                // 存在则将新节点插入到div的下一个兄弟节点之前,即div之后
                divParent.insertBefore(newNode, next);
            } else {
                // 不存在则直接添加到最后,appendChild默认添加到divParent的最后
                divParent.appendChild(newNode);
            }
        }
    }
    pdf(pdfDom, title);
}

// 判断是否需要添加空白div
const isSplit = (nodes, index, pageHeight) => {
    // 计算当前这块dom是否跨越了a4大小,以此分割
    if (nodes[index].offsetTop + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight > pageHeight) {
        return true;
    }
    return false;
}

const pdf = (pdfDom, title) => {
    // 避免出现浏览器滚动条导致的内容不全处理
    document.body.scrollTop = document.documentElement.scrollTop = 0
    //div内部滚动导致内容不全处理
    // document.getElementById('app').style.height = 'auto';
    setTimeout(() => {
        html2canvas(document.getElementById(pdfDom), {
            allowTaint: true,
            scale: 3,  // 按比例增加分辨率
            dpi: 300,  // 分辨率
            // height: document.getElementById('upload').scrollHeight,
            // windowHeight: document.getElementById('upload').scrollHeight
        }).then(canvas => {
            var contentWidth = canvas.width;
            var contentHeight = canvas.height;

            //一页pdf显示html页面生成的canvas高度;
            var pageHeight = contentWidth / 592.28 * 841.89;
            //未生成pdf的html页面高度
            var leftHeight = contentHeight;
            //页面偏移
            var position = 0;
            //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
            var imgWidth = 595.28;
            var imgHeight = 592.28 / contentWidth * contentHeight;

            var pageData = canvas.toDataURL('image/jpeg', 1.0);

            var pdf = new jsPDF('', 'pt', 'a4');

            //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
            //当内容未超过pdf一页显示的范围,无需分页
            if (leftHeight < pageHeight) {
                pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
            } else {
                while (leftHeight > 0) {
                    pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                    leftHeight -= pageHeight;
                    position -= 841.89;
                    //避免添加空白页
                    if (leftHeight > 0) {
                        pdf.addPage();
                    }
                }
            }
            pdf.save(`${title}.pdf`);
        })
    }, 300)
}

const contentRender = () =>{
  return <>
    // item等内容
  </>
}


return (
  <div>
      <Button key="exportPDF" type="primary" loading={pdfLoading} onClick={() => exportPDF('pdfDom', '文件标题')}>
        导出PDF
      </Button>       
      {/* 打印及预览dom */}
      <div className={styles.pageContainer}>
        <div ref={printRef} >
          {contentRender()}
        </div>
      </div>

      {/* pdf导出dom */}
      <div id='pdfDom' className={`${styles.pageContainer} ${styles.noDownload}`} >
        {contentRender()}
      </div>
  </div>
)
css代码

.pageContainer {
    position: relative;
    margin: auto;
    padding: 48px;
    // 预览时候的宽度需要和打印时算上内边距后的正文宽度保持一致
    // 不然会导致计算页码时出现误差
    width: 700px;
    background-color: white;
    font-family: SimSun;
}

.noDownload {
    position: absolute;
    top: 0;
    z-index: -1;
}
以上js代码中,'打印及预览dom'是用于打印预览的(可忽略),所以此处将 pdf下载用的dom复制了一份,设置css .noDownload 隐藏(仅用于下载pdf,不展示给用户)

 

 

 

特殊实现  window.print()

  const printHTML = document.querySelector('#print').innerHTML
    window.document.body.innerHTML = printHTML
    window.print()
    // 打印完成后重新加载页面
    window.location.reload()

 

posted @ 2023-09-19 15:21  镜湖者  阅读(705)  评论(0)    收藏  举报