<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <script src="https://libs.baidu.com/jquery/1.9.1/jquery.js"></script> <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.2/jspdf.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/hanzi-writer@3.0/dist/hanzi-writer.min.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <style> * { margin: 0; padding: 0; } body { font-family: "msyh"; letter-spacing: 0.01px; /*解决jspdf的英文字母间隔问题 */ } table { font-size: 2.5rem; border-spacing: 0.5rem; border: 1px solid black; } table.tb_cn tr td { width: 3.45rem; height: 3.45rem; line-height: 3.45rem; text-align: center; color: rgb(153, 153, 153); border: 1px solid black; } table.tb_cn tr td :nth-child(odd) { height: 2rem; line-height: 2rem; } table tr td:first-child { color: black; } table tr td.with-line { position: relative; } table tr td.with-line::before { content: ""; position: absolute; height: 1px; width: 100%; left: 0; top: 50%; transform: translateY(-50%); border-top: 1px dashed silver; z-index: -1; } table tr td.with-line::after { content: ""; position: absolute; height: 100%; width: 1px; left: 50%; top: 0; transform: translateX(-50%); border-left: 1px dashed silver; z-index: -1; } table.tb_img tr td { width: 6 * 3.45rem; height: 4 * 3.45rem; line-height: 6 * 3.45rem; text-align: center; border: 1px solid black; padding: 0.25rem; } #container { display: flex; } #content-con { width: 210mm; } #content-con .content { height: 300.5mm; } </style> </head> <body> <div id="container"> <div id="content-con"></div> <div id="toolbar"> <textarea id="text" type="textarea" onchange="changeContent()" rows="5">你好世界你好世界你好世界你好世界你好世界</textarea> <br /> <button onclick="exportPdf('content-con')">export Pdf</button> </div> </div> </body> <script> const gbl_param = { page_num: 3, cn_col_num: 12, cn_row_num: 8, char_size: 36, img_bg_id: 0, img_row_num: 2, img_col_num: 3, }; function exportPdf(domId) { html2canvas(document.getElementById(domId)).then((canvas) => { const contentWidth = canvas.width; const contentHeight = canvas.height; console.log("content", contentWidth, contentHeight); const s_w = 595.28; const s_h = 841.89; const sd_w = 7; const pageHeight = (contentWidth / s_w) * s_h; //每页pdf的canvas高度; console.log("page", pageHeight); let leftHeight = contentHeight; //未生成pdf的canvas高度 let position = 10; //pdf页面偏移 //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 const imgWidth = s_w - sd_w; // const imgWidth = s_w; const imgHeight = (imgWidth / contentWidth) * contentHeight; //canvas转为图片后适配A4纸宽度的高度 console.log("img", imgWidth, imgHeight); const pageData = canvas.toDataURL("image/jpeg", 1.0); const pdf = new jspdf.jsPDF("p", "pt", "a4"); //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) //当内容未超过pdf一页显示的范围,无需分页 if (leftHeight < pageHeight) { pdf.addImage(pageData, "JPEG", sd_w, position, imgWidth, imgHeight); } else { while (leftHeight > 0) { pdf.addImage(pageData, "JPEG", sd_w, position, imgWidth, imgHeight); leftHeight -= pageHeight; position -= s_h; console.log("leftHeight", leftHeight); // console.log("position", position); //避免添加空白页 if (leftHeight > 0) { pdf.addPage(); } } let targetPage = pdf.internal.getNumberOfPages(); pdf.deletePage(targetPage); // 删除最后一页 } // pdf.save("xxxx.pdf"); window.open(pdf.output("bloburl")); //预览pdf }); } function renderFanningStrokes(target, strokes) { const char_size = gbl_param.char_size; // createElementNS() 方法可创建带有指定命名空间的元素节点 const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.style.width = char_size + "px"; svg.style.height = char_size + "px"; svg.style.border = "1px solid #EEE"; svg.style.marginRight = "3px"; target.appendChild(svg); const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); const transformData = HanziWriter.getScalingTransform(char_size, char_size); group.setAttributeNS(null, "transform", transformData.transform); svg.appendChild(group); strokes.forEach(function (strokePath) { const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttributeNS(null, "d", strokePath); path.style.fill = "#555"; group.appendChild(path); }); } function getCharOrder(domId, char_cn) { HanziWriter.loadCharacterData(char_cn).then(function (charData) { const target = document.getElementById(domId); target.innerHTML = ""; for (let i = 0; i < charData.strokes.length; i++) { const strokesPortion = charData.strokes.slice(0, i + 1); renderFanningStrokes(target, strokesPortion); } }); } function changeContent() { let str = $("#text").val(); if (str.length < gbl_param.page_num * Math.floor(gbl_param.cn_row_num / 2) || containsNonChineseChar(str)) return; const cn_char_num = Math.floor(gbl_param.cn_row_num / 2); let cnt = 0; for (let i = 0; i < gbl_param.page_num; i++) { for (let j = 0; j < cn_char_num; j++) { getCharOrder("charOrder" + cnt, str[cnt]); $(".content .tb_cn") .eq(i) .find("tr") .eq(2 * j + 1) .find("td") .text(str[cnt]); cnt++; } } } initPage(); changeContent(); function initPage() { const cn_col_num = gbl_param.cn_col_num; const cn_row_num = gbl_param.cn_row_num; $("#content-con").html(""); let cnt = -1; for (let i = 0; i < gbl_param.page_num; i++) { $("#content-con").append( "<div class='content'><table class='tb_cn'>" + Array(cn_row_num) .fill(null) .map((val, idx) => { if (idx % 2 == 0) { cnt++; return `<tr><td id="charOrder${cnt}" colspan="${cn_col_num}"></td></tr>`; } else { return `<tr>${Array(cn_col_num) .fill(null) .map((val, idx) => { return `<td class="with-line"></td>`; }) .join("")} </tr>`; } }) .join("") + "</table><table class='tb_img'>" + Array(gbl_param.img_row_num) .fill(null) .map((val, idx) => { gbl_param.img_bg_id += 1; const url = "./images/test_svg/" + "0".repeat(5 - gbl_param.img_bg_id.toString().length) + gbl_param.img_bg_id + ".svg"; return `<tr>${Array(gbl_param.img_col_num) .fill(null) .map((val, idx) => { return `<td><img src="${url}" width="240" height="240" ></td>`; }) .join("")}</tr>`; }) .join("") + "</table></div>" ); } } function containsNonChineseChar(str) { return /[^\u4e00-\u9fa5]/.test(str); } </script> </html>