You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用html2pdf.js和TypeScript生成PDF时表格文本错位问题

问题描述

我正在构建一个使用html2pdf.js生成PDF文档的TypeScript+React应用。HTML表格在浏览器中渲染正常,但生成的PDF中表格单元格内的文本出现错位(向下偏移),该问题在数值(如价格、总计)中尤为明显。

已尝试的方案:

  • 调整表格样式,设置border-collapse: collapse、vertical-align: middle等
  • 提高html2canvas缩放比例至2
  • 使用white-space: nowrap防止文本换行
  • 检查padding、line-height等CSS属性的兼容性

代码示例(简化版):

import React from 'react';
import html2pdf from 'html2pdf.js';

const Preview = ({ data }) => {
  const generatePDF = () => {
    const element = document.getElementById('invoice-content');
    html2pdf().set({ html2canvas: { scale: 2 } }).from(element).save('invoice.pdf');
  };

  return (
    <div>
      <button onClick={generatePDF}>Download PDF</button>
      <div id="invoice-content">
        <table>
          <thead>
            <tr>
              <th>Column 1</th>
              <th>Column 2</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>123 456 ₸</td>
              <td>789 012 ₸</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  );
};

export default Preview;

疑问:

  1. 如何解决生成的PDF表格中文本向下错位的问题?
  2. 是否有已知的解决方法或能更可靠处理PDF表格渲染的替代库?

解决方案

1. 修复文本错位的可行方法

(1)统一行高与单元格样式

html2canvas对表格单元格的垂直对齐计算经常有偏差,给单元格设置明确的高度、行高和内边距,避免用padding:0:

table {
  border-collapse: collapse;
  width: 100%;
  table-layout: fixed; /* 固定表格布局,避免列宽波动 */
}
td, th {
  vertical-align: middle !important;
  text-align: left;
  padding: 8px 6px; /* 给文本留足够空间 */
  line-height: 1.6; /* 行高与字体大小匹配,比如16px字体对应25.6px行高 */
  height: 44px; /* 明确单元格高度,确保对齐 */
}

(2)切换html2canvas的SVG渲染模式

默认的canvas渲染对文本布局支持有限,启用SVG渲染能大幅提升文本对齐的准确性:

const generatePDF = () => {
  const element = document.getElementById('invoice-content');
  html2pdf().set({
    html2canvas: {
      scale: 2,
      svgRendering: true, // 启用SVG渲染
      useCORS: true
    }
  }).from(element).save('invoice.pdf');
};

(3)用内层容器包裹文本

在单元格内嵌套一个div,用flex布局强制垂直居中,绕过表格本身的对齐问题:

<td>
  <div style={{ 
    display: 'flex', 
    alignItems: 'center', 
    height: '100%',
    width: '100%'
  }}>
    123 456 ₸
  </div>
</td>

(4)清除冲突的继承样式

检查是否有全局样式(比如body的line-height)覆盖了单元格样式,给表格添加隔离类,重置内部样式:

.invoice-table td, .invoice-table th {
  line-height: 1.6 !important;
  vertical-align: middle !important;
  margin: 0;
  padding: 8px 6px;
}

然后给表格加上类:<table className="invoice-table">

2. 更可靠的PDF生成替代库

如果html2pdf的问题无法彻底解决,推荐以下几个专门处理PDF表格的库:

(1)jsPDF + autoTable

这是最成熟的组合,autoTable专门为表格设计,支持各种对齐、样式和复杂布局,完全避免html转译的坑:

import jsPDF from 'jspdf';
import autoTable from 'jspdf-autotable';

const generatePDF = () => {
  const doc = new jsPDF('p', 'mm', 'a4');
  // 生成表格
  autoTable(doc, {
    head: [['Column 1', 'Column 2']],
    body: [['123 456 ₸', '789 012 ₸']],
    styles: {
      valign: 'middle',
      halign: 'left',
      cellPadding: 8,
      fontSize: 12
    },
    headStyles: {
      fillColor: '#f0f0f0'
    }
  });
  doc.save('invoice.pdf');
};

(2)react-pdf

React专属的声明式PDF库,直接用组件定义PDF内容,完全基于PDF规范渲染,没有html转译的兼容性问题:

import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
import { PDFDownloadLink } from '@react-pdf/renderer';

const styles = StyleSheet.create({
  page: { padding: 20 },
  table: { width: '100%', marginBottom: 20 },
  tableRow: { flexDirection: 'row', borderBottom: '1px solid #eee' },
  tableCell: { flex: 1, padding: 8, textAlign: 'left', justifyContent: 'center' },
  headerCell: { flex: 1, padding: 8, fontWeight: 'bold', backgroundColor: '#f0f0f0' }
});

const InvoiceDocument = ({ data }) => (
  <Document>
    <Page style={styles.page}>
      <View style={styles.table}>
        <View style={styles.tableRow}>
          <Text style={styles.headerCell}>Column 1</Text>
          <Text style={styles.headerCell}>Column 2</Text>
        </View>
        <View style={styles.tableRow}>
          <Text style={styles.tableCell}>123 456 ₸</Text>
          <Text style={styles.tableCell}>789 012 ₸</Text>
        </View>
      </View>
    </Page>
  </Document>
);

// 在组件中使用下载链接
const Preview = ({ data }) => (
  <div>
    <PDFDownloadLink document={<InvoiceDocument data={data} />} fileName="invoice.pdf">
      {({ blob, url, loading, error }) => loading ? 'Generating PDF...' : 'Download PDF'}
    </PDFDownloadLink>
  </div>
);

(3)Puppeteer

用无头Chrome直接渲染页面并导出PDF,完全和浏览器效果一致,适合需要1:1还原复杂页面的场景(需要后端支持或本地Chrome环境):

// 后端Node.js示例,前端可通过API调用
const puppeteer = require('puppeteer');

async function generateInvoicePDF(htmlContent) {
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();
  await page.setContent(htmlContent, { waitUntil: 'networkidle0' });
  const pdfBuffer = await page.pdf({
    format: 'A4',
    printBackground: true,
    margin: { top: 20, right: 20, bottom: 20, left: 20 }
  });
  await browser.close();
  return pdfBuffer;
}

内容的提问来源于stack exchange,提问作者Tr007ll

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:36:01