JS纯前端导出PDF及分页和使用window.print()保存PDF

	最近由于项目要求需要将导出PDF类文件,其中涉及到固定表头,翻页,样式调整等问题
一开始选择了网上较多讲解的使用html2canvas.js和jspdf.js先转图片再转PDF的方法。
 var xsxf = document.getElementById("export_content");
 html2canvas(
     xsxf,
     //document.getElementById("export_content"),
     {
         dpi: 172,//导出pdf清晰度
         //proxy: "string",
         onrendered: function (canvas) {
             var contentWidth = canvas.width;
             var contentHeight = canvas.height;
             //一页pdf显示html页面生成的canvas高度;
             var pageHeight = 840;
             //var pageHeight = contentWidth / 575 * 840;
             //未生成pdf的html页面高度
             var leftHeight = contentHeight;
             //pdf页面偏移
             var position = 0;
             //html页面生成的canvas在pdf中图片的宽高(a4纸的尺寸[595.28,841.89])
             var imgWidth = 595.28;
             var imgHeight = 592.28 / contentWidth * contentHeight;
    
             var pageData;
             var pdf = new jsPDF('', 'pt', 'a4');
             //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
             //当内容未超过pdf一页显示的范围,无需分页
            if (leftHeight < pageHeight) {
                 pageData = canvas.toDataURL('image/jpeg', 1.0);
                 pdf.addImage(pageData, 'JPEG', 10, 5, imgWidth - 20, imgHeight - 10);
                
             } else {
                 while (leftHeight > 0) {
                     pageData = canvas.toDataURL('image/jpeg', 1.0);
                     pdf.addImage(pageData, 'JPEG', 10, position + 5, imgWidth - 20, imgHeight - 10)
                     leftHeight -= pageHeight;
                     position -= 841.89;
                     //避免添加空白页
                     if (leftHeight > 0) {
                         pdf.addPage();
                     }
                 }
             }
             pdf.save(filename);
         },
         //背景设为白色(默认为黑色)
         background: "#fff"
     });

但很快发现当表格数据量大时出现翻页问题,所以试着先将表头固定数据取出,表体数据进行分割拼接表头

 //大于一页分页表头固定
        function reFullHtml(wb, filename, _tableHead, rowNum, pageHeight) {
            var tableH = document.querySelector('#table_content').scrollHeight + 70;
            var miH = (pageHeight - tableH);       //详细
            var trL = $('#mini-grid-table1 > tbody>tr').length;
            var max = parseInt(miH / 34);
            var firstNum = rowNum - 2 + max;

            var page = Math.ceil(trL / max);
            var brhtml = "</table></br></br></br></br></br>";
            //PL表尾
            if (rowNum == 12 && firstNum > (trL + rowNum - 5)) {
                firstNum = trL + rowNum - 6;
                page = Math.ceil(trL / (max - 5));
                brhtml = brhtml + "</br></br></br></br></br></br></br></br></br>";
            }
            //CI表尾(特殊德国小家电)
            if (rowNum == 14 && firstNum > (trL + rowNum - 1)) {
                firstNum = trL + rowNum - 2;
                page = Math.ceil(trL / (max - 1));
                brhtml = brhtml + "</br></br>";
            }
            //判断一页内行数高度+表头高是否大于页高
            if ((firstNum - rowNum) * 34 + 75 > miH) {
                firstNum -= 1;
                brhtml = '</table></br></br></br></br></br></br></br>';
            }
            var beforNum = firstNum;
            var newWb = wb;
            var pNum = firstNum - rowNum - 2;
            var syNum = pageHeight - (pNum + 1) * 34 - tableH;
            var nbrHtml = "</table>"
            for (var c = 0; c < Math.ceil(syNum / 20); c++) {
                nbrHtml += "</br>"
            }
            for (var i = 0; i < page - 1; i++) {
                if ((firstNum - beforNum) * 34 + 75 > miH) {
                    firstNum -= 1;
                    brhtml = '</table></br></br></br></br></br></br></br>';
                }
                beforNum = firstNum;
                var reg = "<tr><td style='text-align: center; border-bottom: 1px solid #000000;  border-right: 1px solid #000000; font-size: 10px; font-family: 微软雅黑; word-wrap:break-word; height:33px;' t='[a-z]' id='sjs-A" + firstNum + "'>.*";
                var res = newWb.match(reg);
                if (res != null) {
                    if (res.length > 0) {
                        var newHead = newWb.replace(res[0], brhtml);
                        newWb = newHead + _tableHead + res[0];
                    }
                }
                firstNum += pNum;
                brhtml = nbrHtml;
            }
            if (firstNum < (trL + rowNum)) {
                if (rowNum == 12) {
                    firstNum = trL + rowNum - 6;
                    brhtml = "</table></br></br></br></br></br></br></br></br></br></br></br></br></br>";
                }
                else if (rowNum == 14) {
                    firstNum = trL + rowNum - 2;
                    brhtml = "</table></br></br></br></br></br></br>";
                }
                var reg1 = "<tr><td style='text-align: center; border-bottom: 1px solid #000000;  border-right: 1px solid #000000; font-size: 10px; font-family: 微软雅黑; word-wrap:break-word; height:33px;' t='[a-z]' id='sjs-A" + firstNum + "'>.*";
                var res1 = newWb.match(reg1);
                if (res1 != null) {
                    if (res1.length > 0) {
                        var newHead = newWb.replace(res1[0], brhtml);
                        newWb = newHead + _tableHead + res1[0];
                    }
                }
            }
            var xsxf = document.getElementById("export_content");
            xsxf.innerHTML = "</br>" +  newWb;
            rowNum = 1;
            if (newWb != "") {
                //var pageWidth = document.querySelector("#mini-grid-table1").scrollWidth;

                for (var p = 0; p < document.querySelectorAll("#sjs-A1").length; p++) {
                    document.querySelectorAll("#sjs-A1")[p].width = (pageWidth - 20) + "px";

                    document.querySelectorAll("#sjs-A5")[p].width = (pageWidth - 20) / 2 + "px";
                    document.querySelectorAll("#sjs-E5")[p].width = (pageWidth - 20) / 2 + "px";

                }
                downPDF(newWb, filename, _tableHead, rowNum);
            }

        }

分页解决完毕后,业务部门使用后反馈PDF过于模糊和数据无法复制,无奈寻找其他方法。
最后确定使用window.print()原生打印方法来保存。
其中碰到几个坑:
1、当表格宽度大于A4纸时,表格列宽会被挤压变现导致之前设置的列宽无效从而使分页出现问题
2、分页容纳行数算法和使用wp打印显示行数不一致,固尝试多次后固定行数
解决方案:
1、分页属性:style=‘page-break-after:always’ 分页属性 table 加上后会自动分页
2、设置表格宽度,其中表格宽最大设置到1000,大于1000时会变形

table {
  width:750px;
}

3、特殊列设置固定宽(列文字多的),其他使用百分比

function downPDF(wb, filename, _tableHead, rowNum, dataNum) {
	var xsxf = document.getElementById("export_content");
	  html2canvas(
	      xsxf,
	      //document.getElementById("export_content"),
	      {
	          //dpi: 172,//导出pdf清晰度
	          dpi: 144,
	          //proxy: "string",
	          onrendered: function (canvas) {
	              var contentWidth = canvas.width;
	              var contentHeight = canvas.height;
	              //一页pdf显示html页面生成的canvas高度;
	              var pageHeight = 840;
	              //var pageHeight = contentWidth / 575 * 840;
	              //未生成pdf的html页面高度
	              var leftHeight = contentHeight;
	              //pdf页面偏移
	              var position = 0;
	              //html页面生成的canvas在pdf中图片的宽高(a4纸的尺寸[595.28,841.89])
	              var imgWidth = 595.28;
	              var imgHeight = 592.28 / contentWidth * contentHeight;
	              var pageData;
	              var pdf = new jsPDF('', 'pt', 'a4');
	              if (leftHeight > pageHeight) {
	                  if (rowNum != 1) {
	                      reFullHtml(wb, filename, _tableHead, rowNum, dataNum);
	                      return;
	                  }
	              }
	              //20210826 hsj 原转PDF转图片失真严重,直接调用window打印方法保存
	              //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
	              //当内容未超过pdf一页显示的范围,无需分页
	              //if (leftHeight < pageHeight) {
	              //    pageData = canvas.toDataURL('image/jpeg', 1.0);
	              //    pdf.addImage(pageData, 'JPEG', 10, 5, imgWidth - 20, imgHeight - 10);
	                 
	              //} else {
	              //    while (leftHeight > 0) {
	              //        pageData = canvas.toDataURL('image/jpeg', 1.0);
	              //        pdf.addImage(pageData, 'JPEG', 10, position + 5, imgWidth - 20, imgHeight - 10)
	              //        leftHeight -= pageHeight;
	              //        position -= 841.89;
	              //        //避免添加空白页
	              //        if (leftHeight > 0) {
	              //            pdf.addPage();
	              //        }
	              //    }
	              //}
	              //pdf.save(filename);
	              window.print();
	          },
	          //背景设为白色(默认为黑色)
	          background: "#fff"
	      });
}
  //大于一页分页表头固定
        function reFullHtml(wb, filename, _tableHead, rowNum, dataNum) {
            var trL = $('#mini-grid-table1 > tbody>tr').length;
            var max = 20;
            if (dataNum>10) {
                max = 27;
            }
            else if (dataNum == 10){
                max = 25;
            }
            else if (dataNum >7) {
                max = 22;
            }
            var firstNum = rowNum + 2 + max;
            var page = Math.ceil(trL / max);
            var brhtml = "</table>";
            var newWb = wb;
            for (var i = 0; i < page - 1; i++) {
                var reg = "<tr><td style='text-align: center; border-bottom: 1px solid #000000;  border-right: 1px solid #000000; font-size: 10px; font-family: 微软雅黑; word-wrap:break-word; height:33px;' t='[a-z]' id='sjs-A" + firstNum + "'>.*";
                var res = newWb.match(reg);
                if (res != null) {
                    if (res.length > 0) {
                        var newHead = newWb.replace(res[0], brhtml);
                        //style='page-break-after:always' 分页属性
                        newWb = newHead + "</div><div style='page-break-after:always'>" + _tableHead + res[0];
                    }
                }
                else {
                    if (rowNum == 12) {
                        firstNum = trL + rowNum - 5;
                        brhtml = "</table>";
                    }
                    else if (rowNum == 14) {
                        firstNum = trL + rowNum - 1;
                        brhtml = "</table>";
                    }
                    var reg1 = "<tr><td style='text-align: center; border-bottom: 1px solid #000000;  border-right: 1px solid #000000; font-size: 10px; font-family: 微软雅黑; word-wrap:break-word; height:33px;' t='[a-z]' id='sjs-A" + firstNum + "'>.*";
                    var res1 = newWb.match(reg1);
                    if (res1 != null) {
                        if (res1.length > 0) {
                            var newHead = newWb.replace(res1[0], brhtml);
                            newWb = newHead + "</div><div style='page-break-after:always'>" + _tableHead + res1[0];
                        }
                    }
                }
                firstNum += max;
            }
            if (firstNum < (trL + rowNum)) {
                if (rowNum == 12) {
                    firstNum = trL + rowNum - 5;
                    brhtml = "</table>";
                }
                else if (rowNum == 14) {
                    firstNum = trL + rowNum - 2;
                    brhtml = "</table>";
                }
                var reg1 = "<tr><td style='text-align: center; border-bottom: 1px solid #000000;  border-right: 1px solid #000000; font-size: 10px; font-family: 微软雅黑; word-wrap:break-word; height:33px;' t='[a-z]' id='sjs-A" + firstNum + "'>.*";
                var res1 = newWb.match(reg1);
                if (res1 != null) {
                    if (res1.length > 0) {
                        var newHead = newWb.replace(res1[0], brhtml);
                        newWb = newHead + "</div><div style='page-break-after:always'>" + _tableHead + res1[0];
                    }
                }
            }
            var xsxf = document.getElementById("export_content");
            xsxf.innerHTML = "<div style='page-break-after:always'>" + newWb + "</div>";
            rowNum = 1;
            if (newWb != "") {
                if (dataNum >= 10) {
                    for (var n = 0; n < document.querySelectorAll("table").length; n++) {
                        document.querySelectorAll("table")[n].style.width = '900px';
                        document.querySelectorAll("table")[n].style.width = '900px';
                    }
                }
                for (var p = 0; p < document.querySelectorAll("#sjs-A1").length; p++) {
                    document.querySelectorAll("#sjs-A1")[p].width = (pageWidth - 20) + "px";
                    document.querySelectorAll("#sjs-A5")[p].width = (pageWidth - 20) / 2 + "px";
                    document.querySelectorAll("#sjs-E5")[p].width = (pageWidth - 20) / 2 + "px";
                    
                }
                downPDF(newWb, filename, _tableHead, rowNum);
            }
           
        }

效果
JS纯前端导出PDF及分页和使用window.print()保存PDF
JS纯前端导出PDF及分页和使用window.print()保存PDF
总结:总体来说还是使用最开始导出PDF的思路,先在html中解决分页问题后导出,不过放弃转成图片方式而直接使用原生方法。整体解决了分页问题和不清晰无法复制等问题。

上一篇:UReport2分页统计的sql语句


下一篇:Oracle中的伪列