使用html2pdf.js实现目录准确动态页码的问题求助
html2pdf.js实现目录准确动态页码的问题求助
大家好,我现在在尝试用html2pdf.js生成带目录的PDF,要求目录里的页码能和实际章节在PDF中的位置精准对应,但目前的实现有问题——目录里的页码和最终PDF里章节的实际位置对不上,完全没达到预期效果。
我的需求和预期PDF结构
我想要的PDF结构是这样的:
- 封面页(无页码)
- 目录页(页码ii)
- 章节1(页码1)
- 章节2(页码3)
- ……以此类推
我试过的解决方案:双次生成法
我本来想通过两次生成PDF的思路来解决页码匹配问题:
- 第一次生成PDF,获取各个章节在PDF中的位置
- 用获取到的页码更新DOM里的目录内容
- 第二次基于更新后的DOM生成最终的PDF
遇到的核心问题
第一次生成后提取的章节页码,更新到目录后,最终PDF里的章节实际位置和目录标注的还是对不上,页码偏差完全没解决。
我的代码实现
下面是我目前的完整代码:
const convertToPdf = async () => { const { default: html2pdf } = await import('html2pdf.js'); if (typeof window === 'undefined' || typeof document === 'undefined') return; const content = document.getElementById('pdf-content'); const opt = { filename: 'Incident Report Analysis.pdf', pagebreak: { mode: ['css', 'legacy'], before: '.avoid-page-break', avoid: ['div', '#breakdata', 'tr', 'table'] }, image: { type: 'jpeg', quality: 1 }, html2canvas: { scale: 1.4, logging: true }, jsPDF: { unit: 'in', format: 'A3', orientation: 'portrait' }, margin: [0.1, 0, 0.30, 0], }; // 第一次生成PDF,提取章节位置 const firstPassPdf = await html2pdf().set(opt).from(content).toPdf().get('pdf'); await new Promise(resolve => setTimeout(resolve, 500)); const extractedSections = extractSectionPositions(firstPassPdf, content, opt.html2canvas.scale); // 调整页码(+1是因为要加上目录页的偏移) const updatedSections = extractedSections.map(section => ({ ...section, page: section.page + 1 })); // 更新DOM中的目录页码 updatedSections.forEach(section => { const span = document.querySelector(`.toc-page[data-target="${section.title}"]`); if (span) { span.textContent = section.page; } }); // 强制React更新DOM(等待渲染完成) setSections(updatedSections); await new Promise(resolve => setTimeout(resolve, 500)); // 生成最终的PDF const finalContent = document.getElementById('pdf-content'); const finalPdf = await html2pdf().set(opt).from(finalContent).toPdf().get('pdf'); // 添加页脚和其他元素 const totalPages = finalPdf.internal.getNumberOfPages(); const pageWidth = finalPdf.internal.pageSize.width; const pageHeight = finalPdf.internal.pageSize.height; const footerHeight = 0.4; const footerYPosition = pageHeight - footerHeight; const centerYPosition = footerYPosition + footerHeight / 2; for (let i = 1; i <= totalPages; i++) { finalPdf.setPage(i); finalPdf.setFontSize(12); finalPdf.setFont('helvetica'); if (i === 2) { // 目录页添加标题 finalPdf.setFontSize(14); finalPdf.setTextColor(0, 0, 0); finalPdf.text("Table of Contents", 0.3, 0.5); } if (i > 1) { // 非封面页添加页脚 finalPdf.setFillColor(51, 51, 51); finalPdf.rect(0, footerYPosition, pageWidth, footerHeight, 'F'); finalPdf.setTextColor(255, 255, 255); finalPdf.text('WWW.ICORPSECURITY.COM.AU', 0.3, centerYPosition, { align: 'left', baseline: 'middle' }); finalPdf.text(`Page ${i} of ${totalPages}`, pageWidth - 0.3, centerYPosition, { align: 'right', baseline: 'middle' }); } else { // 封面页的页脚样式 finalPdf.setFillColor(245, 245, 246); finalPdf.rect(0, footerYPosition, pageWidth, footerHeight, 'F'); } } finalPdf.save(`Incident Analysis (${dayjs(formattedStartDateRange).format('DD/MM/YYYY')} - ${dayjs(formattedEndDateRange || formattedStartDateRange).format('DD/MM/YYYY')}).pdf`); if (onDownloadComplete) { onDownloadComplete(); } }; const extractSectionPositions = (pdf, content, scale) => { const sectionElements = content.querySelectorAll('[data-section]'); const pixelsPerInch = 96; const pixelsPerPage = (pdf.internal.pageSize.height * pixelsPerInch) / scale; const sections = Array.from(sectionElements).map(el => { const top = el.offsetTop; const pageNum = Math.floor(top / pixelsPerPage) + 1; console.log(pageNum, "pageNum"); return { title: el.getAttribute('data-section'), page: pageNum }; }).sort((a, b) => a.page - b.page); return sections; };
有没有大佬能帮我看看问题出在哪?或者有没有更可靠的方法来实现html2pdf.js的动态目录页码匹配?
内容来源于stack exchange
相关产品推荐
相关产品推荐

