前端页面转pdf文字在分页处截断问题解决

概要

在实际使用中发现自定义富文本内容由于文字大小问题,格式问题,分页大小问题等,会导致导出内容在分页处有不同程度的截断现象。

主要处理的难点

图片、文字被分割的现象,即分页处理

技术细节

提示:这里可以添加技术细节
网上查询资料即可发现前端导出pdf使用html2canvas和jspdf即可实现简单pdf导出,这块不再赘述。主要讲述分页处截断问题处理的思路,假设pdf导出页面固定高度为a,插入的每个块元素的高度为b,插入的元素距离顶部的高度为c,当前页码为p。即可得到公式b+c>a*p时是分页截断处,此时的块元素将会有被从中间截断的风险。以下称这个有截断风险的元素为截断元素。此时有两种方案:
方案一: 核心:在页面无法放入截断元素时将页面用空白元素补空,并将截断元素挤下去 。在截断元素前插入a+p-c高度的空白元素。将截断元素顶到下一页的开始处。此方法如果遇到纯文字的极端情况会有比较大的留白,但是如果被截断元素是图片元素适用此方式
方案二: 核心:将截断元素从中间分开,然后在分页处插入空白元素。在截断元素处再插入一个截断元素,并且根据a+p-c的高度,以及此截断元素的行高计算,比如行高为36,就设置将页面剩余高度(a+p-c)分开两部分,第一部分为Math.floor((a+p-c)/36)*36 (n为整数),第二部分为剩余的高度( 剩余高度小于36,故完整放入一行文字,此处高度为文字产生截断的真正原因,将此处做空白处理,即可避免。此处也是该方案的关键),然后将新插入的截断元素进行marginTop为- Math.floor((a+p-c)/36)*36 (n为整数)高度的元素。此方法适用整段文字的场景。分页处留白也都是小于文字行高的空白。让人更容易接受。

代码实现

方案二实际为方案一的优化。以下为方案二的核心代码

// 判断是否需要添加空白div
const isSplit =  (nodes, index, pageHeight) => {
    let x1 = document.getElementById('itemsss').offsetTop
    let x = document.getElementById('itemss').offsetTop + x1
    // 计算当前这块dom是否跨越了a4大小,以此分割
    if (nodes[index].offsetTop  + x + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + x + nodes[index + 1].offsetHeight > pageHeight) {
        return true;
    }
    return false;
}
export const outPutPdfFn =  async () => {
    // $myLoading 自定义等待动画组件,实现导出事件的异步等待交互
    // dom的id。
    let pageHeight = 1137;
    const pdfDom = document.getElementById('pdfDom') // 需要导出pdf的内容
    let lableListID = pdfDom.getElementsByTagName('p'); // 可能被截断的所有块元素
    // 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割
    for (let i = 0; i < lableListID.length; i++) {
        // 获取分割dom,此处为class类名为item的dom
        let x1 = document.getElementById('item').offsetTop
        let x2 = document.getElementById('itemss').offsetTop + x1
        let multiple = Math.ceil((lableListID[i].offsetTop + x2 + lableListID[i].offsetHeight) / pageHeight);
        let x = x2 - 21*(multiple - 1) // 此处因为页面高度不准加的随机数
        // 判断是否该分页
        if (isSplit(lableListID, i, multiple * pageHeight)) {
            let divParent = lableListID[i].parentNode; // 获取该div的父节点
            let newNode = document.createElement('div');
            newNode.className = 'emptyDiv';
            newNode.style.background = 'transparent';
            newNode.style.overflow = 'hidden';
            let _H = multiple * pageHeight - (lableListID[i].offsetTop + x + lableListID[i].offsetHeight);
            const a = parseInt(Math.floor(_H/36))*36
            newNode.style.height = _H + 'px';
            newNode.style.width = '100%';
            let next = lableListID[i].nextSibling; // 获取div的下一个兄弟节点
            // 判断兄弟节点是否存在
            if (next) {
              if(a>0){
                newNode.innerHTML = `<p style="height: ${a}px;overflow:hidden;margin-top:0;text-indent: ${getComputedStyle(next, false)['text-indent']};">${next.innerHTML}</p>`
                console.log(next.offsetHeight, next.getAttribute('style'), getComputedStyle(next, false)['text-indent'])
                next.style=`overflow:hidden;height: ${next.offsetHeight-a}px;`
                next.innerHTML = `<p style="margin-top: -${a}px;text-indent: ${getComputedStyle(next, false)['text-indent']};">${next.innerHTML}</p>`
              }
                // 存在则将新节点插入到div的下一个兄弟节点之前,即div之后
                divParent.insertBefore(newNode, next);
            } 
            else {
                // 不存在则直接添加到最后,appendChild默认添加到divParent的最后
                divParent.appendChild(newNode);
            }
        }
    }
  }
export const getPdf = async (ref, footer, header, loading) => {
    await outPutPdfFn(ref)
    html2canvas(ref, {
          allowTaint: false,
          taintTest: false,
          logging: false,
          useCORS: true,
          dpi: 4, //将分辨率提高到特定的DPI 提高四倍
          scale:4 //按比例增加分辨率
      }).then(async canvas=>{
          var pdf = new JsPDF('p', 'mm', 'a4');    //A4纸,纵向
          var ctx = canvas.getContext('2d'),
              a4w = 160, a4h = 247,    //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
              imgHeight = Math.floor(a4h * canvas.width / a4w),    //按A4显示比例换算一页图像的像素高度
              renderedHeight = 0;
          let i = 0
          let pages = Math.ceil(canvas.height/imgHeight)
          while(renderedHeight < canvas.height) {
            i++
              var page = document.createElement("canvas");
              page.width = canvas.width;
              page.height = Math.min(imgHeight, canvas.height - renderedHeight);//可能内容不足一页
  
              //用getImageData剪裁指定区域,并画到前面创建的canvas对象中
              page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
              pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 25, 25, a4w, Math.min(a4h, a4w * page.height / page.width));    //添加图像到页面,保留10mm边距
  
              if(i>1){
                const canvas2 = await html2canvas(header, {
                  // allowTaint: true, // 允许渲染跨域图片
                  dpi: 0.5, //按比例增加分辨率
                   useCORS: true,// 允许跨域
                });
                pdf.addImage(canvas2.toDataURL('image/jpeg', 1.0), 'JPEG', 25, 24.9, a4w, 0.1)
              }
              // 此处为页脚添加 
              footer.querySelector('.pdf-footer-page').innerText = i;
              footer.querySelector('.pdf-footer-page-count').innerText = pages;
              const canvas1 = await html2canvas(footer, {
                // allowTaint: true, // 允许渲染跨域图片
                dpi: 4,
                scale: 4,  // 增加清晰度
                useCORS: true,// 允许跨域
                allowTaint: false,
                taintTest: false,
                logging: false,
                useCORS: true,
              });
              renderedHeight += imgHeight;
              pdf.addImage(canvas1.toDataURL('image/jpeg', 1.0), 'JPEG', 25, 272, a4w, 9)
              if(renderedHeight < canvas.height) {
                  pdf.addPage();//如果后面还有内容,添加一个空页
              }
              // delete page;
          }
          //保存文件
          pdf.save('测试.pdf')
          loading.exportLoading = false
        // 以下方法实现预览
        //   const blob = pdf.output("bloburl");// 转base64
        //   window.open(blob)
      })
  }
  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值