dompdf.js 分页功能完整实现指南

一、分页基础配置

要让 dompdf.js 实现分页,必须满足两个前提条件:

1. 启用分页选项

javascript 复制代码
const options = {
  pagination: true,  // 关键:必须设为 true
  format: 'a4'       // 指定纸张大小
}

2. 容器宽度匹配纸张尺寸

A4 纸在 96dpi 下的宽度是 794px。容器宽度必须精确匹配:

css 复制代码
.resume-paper {
  width: 794px;
  /* 其他样式 */
}

原理:dompdf.js 根据容器宽度计算内容是否溢出,溢出时自动分页。如果宽度不匹配,分页计算会出错。

二、两种分页控制方式

方式 1:自动分页

当内容高度超过单页高度(A4 纸 1123px)时,dompdf.js 自动在合适位置分页。

适用场景:内容较多,不需要精确控制分页位置。

方式 2:强制分页

在特定元素前强制分页,即使该元素位于页面中间。

实现方法

  1. 给目标元素添加 pageBreak HTML 属性
  2. 在配置中指定选择器
html 复制代码
<!-- 分页标记元素 -->
<div pageBreak data-page-break="true"></div>
javascript 复制代码
const options = {
  pagination: true,
  pageBreak: {
    before: '[data-page-break="true"]'  // CSS 选择器
  }
}

三、防止元素被分页切断

对于某些元素(如标题、段落),不希望它们在分页时被切断成两部分。使用 divisionDisable 属性:

html 复制代码
<div class="section" divisionDisable>
  <h2>工作经历</h2>
  <p>详细内容...</p>
</div>

原理 :dompdf.js 通过 el.hasAttribute('divisionDisable') 检测此属性。当元素带有此属性时,如果它即将被分页切断,会将其整体移到下一页。

四、完整配置示例

基于当前项目的实际配置:

javascript 复制代码
// 1. 加载字体(中文字体支持)
const fontResponse = await fetch('/fonts/SourceHanSansSC-Regular.ttf')
const fontBuffer = await fontResponse.arrayBuffer()

// 2. 配置选项
const options = {
  format: 'a4',
  pagination: true,           // 启用分页
  compress: true,             // 压缩 PDF
  useCORS: true,              // 允许跨域图片
  
  // 强制分页配置
  pageBreak: {
    before: '[data-page-break="true"]'  // 在 data-page-break="true" 元素前分页
  },
  
  // 中文字体配置
  fontConfig: {
    fontFamily: 'SourceHanSansSC-Regular',
    fontBytes: new Uint8Array(fontBuffer),
    fontStyle: 'normal',
    fontWeight: 400
  },
  
  // 页眉页脚配置
  pageConfig: {
    header: { content: '', height: 20 },  // 透明页眉占位
    footer: { content: '', height: 0 }    // 无页脚
  }
}

// 3. 调用 dompdf
const blob = await dompdf(paperEl, options)

五、关键 HTML 结构

1. 分页标记元素

html 复制代码
<div 
  class="page-break-marker"
  pageBreak
  data-page-break="true"
>
  <!-- 内容在导出时会被清空 -->
</div>

属性说明

  • pageBreak:告诉 dompdf.js 这是分页点
  • data-page-break="true":供 pageBreak.before 选择器匹配

2. 防切断元素

html 复制代码
<div class="section" divisionDisable>
  <!-- 内容不会在中间被切断 -->
</div>

六、导出时的特殊处理

分页标记在编辑界面显示,但在 PDF 中不应显示。需要特殊处理:

javascript 复制代码
const exportPDF = async () => {
  let savedContents = []  // 保存标记内容
  
  try {
    // 1. 清除标记内容
    const markers = paperEl.querySelectorAll('.page-break-marker')
    markers.forEach((marker, i) => {
      savedContents[i] = marker.innerHTML
      marker.innerHTML = ''  // 清空内容,但保留 div 和属性
    })
    
    // 2. 生成 PDF
    const blob = await dompdf(paperEl, options)
    
  } finally {
    // 3. 恢复标记内容
    const markers = paperEl.querySelectorAll('.page-break-marker')
    markers.forEach((marker, i) => {
      if (savedContents[i] !== undefined) {
        marker.innerHTML = savedContents[i]
      }
    })
  }
}

为什么不能用 CSS 隐藏?

dompdf.js 对 CSS 的支持有限,特别是:

  • visibility: hidden 可能被忽略
  • height: 0; overflow: hidden 可能失效
  • display: none 会完全忽略元素

最可靠的方法是直接清空 innerHTML,保留元素结构和属性。

七、常见问题和解决方案

问题 原因 解决方案
分页不生效 pagination 未设为 true 设置 pagination: true
分页位置不准 容器宽度不匹配 设置 width: 794px(A4@96dpi)
元素被切断 缺少 divisionDisable 属性 添加 HTML 属性 divisionDisable
强制分页无效 选择器不匹配 确保 pageBreak.before 选择器正确
v-show 导致失效 display: none 让元素被忽略 改用 CSS 类控制可见性
标记文本显示 CSS 隐藏被忽略 导出前清空 innerHTML

八、调试技巧

  1. 检查元素属性 :确保 pageBreakdivisionDisable 等是 HTML 属性,不是 CSS 类
  2. 验证选择器 :在浏览器控制台测试 document.querySelectorAll('[data-page-break="true"]')
  3. 容器宽度 :用开发者工具检查 .resume-paper 的计算宽度是否为 794px
  4. 分页预览:先在小内容上测试,确保分页逻辑正确

九、完整代码示例

vue 复制代码
<template>
  <div class="resume-paper" ref="resumeRef">
    <!-- 头部(防止被切断) -->
    <div class="header-section" divisionDisable>
      <h1>姓名</h1>
      <p>联系方式</p>
    </div>
    
    <!-- 第一个分页点 -->
    <div 
      v-if="pageBreaks.includes(1)"
      class="page-break-marker"
      pageBreak
      data-page-break="true"
    >
      分页标记
    </div>
    
    <!-- 工作经历(防止被切断) -->
    <div class="section" divisionDisable>
      <h2>工作经历</h2>
      <p>详细内容...</p>
    </div>
  </div>
</template>
javascript 复制代码
// useResume.js
const exportPDF = async () => {
  // ... 前面的代码
  
  const options = {
    format: 'a4',
    pagination: true,
    pageBreak: {
      before: '[data-page-break="true"]'
    },
    // ... 其他配置
  }
  
  const blob = await dompdf(paperEl, options)
}

十、总结

dompdf.js 的分页功能依赖于:

  1. 正确的配置pagination: true 和匹配的容器宽度
  2. HTML 属性pageBreakdivisionDisable(不是 CSS 类)
  3. 选择器配置pageBreak.before 指定分页点
  4. 特殊处理:导出时清空标记内容,避免显示问题

记住:dompdf.js 对 CSS 支持有限,直接操作 DOM 是最可靠的方式。

相关推荐
wordbaby1 小时前
App 热更新(OTA)原理深解 —— 以 React Native 为例
前端·react native
再吃一根胡萝卜1 小时前
Vue + dompdf.js 实现简历分页导出的完整踩坑记录
前端
其美杰布-富贵-李1 小时前
第 6 篇:相机与 OrbitControls
前端·javascript·three.js
lichenyang4531 小时前
从图片生成任务到用户隔离:AIGC Creative Studio 后端与 PostgreSQL 建模实践
前端·后端
小黑技术栈2 小时前
关于前端的一些话
前端
无人生还2 小时前
从 Vue3 到 React · 快速上手系列第 11 篇:状态管理
前端·vue.js·react.js
GuWenyue2 小时前
90%前端写React TS都踩坑!一套父子组件+Hooks完整实战,彻底搞懂类型约束
前端·react.js
Hilaku3 小时前
当 AI 一天写完一周的代码,我们还剩什么优势?
前端·javascript·程序员
尘世中一位迷途小书童3 小时前
无人机航线里的云台角度怎么调?Cesium 做了个视锥 + 鹰眼联动
前端·javascript·数据可视化