前端导出pdf,所见即所得

news2025/2/11 20:16:14

一、推荐方案:html2canvas + jsPDF(图片式PDF)

javascript

import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

const exportPDF = async (elementId, fileName) => {
  const element = document.getElementById(elementId);
  
  // 1. 将DOM转为Canvas
  const canvas = await html2canvas(element, {
    scale: 2, // 提升清晰度
    useCORS: true, // 允许跨域图片
    logging: true, // 调试时开启
  });

  // 2. Canvas转图片数据
  const imgData = canvas.toDataURL('image/png', 1.0);
  
  // 3. 计算PDF尺寸
  const pdf = new jsPDF('p', 'mm', 'a4');
  const pageWidth = pdf.internal.pageSize.getWidth();
  const pageHeight = pdf.internal.pageSize.getHeight();
  const imgRatio = canvas.width / canvas.height;
  
  // 4. 自动适应页面大小
  let imgHeight = pageHeight;
  let imgWidth = pageWidth;
  if (pageWidth / imgRatio < pageHeight) {
    imgWidth = pageWidth;
    imgHeight = imgWidth / imgRatio;
  } else {
    imgHeight = pageHeight;
    imgWidth = imgHeight * imgRatio;
  }

  // 5. 添加图片到PDF
  pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight);
  
  // 6. 保存文件
  pdf.save(`${fileName}.pdf`);
};

// 使用示例
exportPDF('export-container', 'report');
 

二、进阶方案:jsPDF内置HTML渲染(矢量文本)

javascript

import { jsPDF } from "jspdf";

const exportPDF = async () => {
  const doc = new jsPDF('p', 'pt', 'a4');
  
  // 1. 获取需要导出的HTML元素
  const element = document.getElementById('content');
  
  // 2. 直接渲染HTML到PDF(需要配合html2canvas)
  await doc.html(element, {
    callback: (doc) => doc.save('document.pdf'),
    margin: [20, 20, 20, 20],
    autoPaging: 'text',
    width: 170, // 有效内容区宽度(mm)
    windowWidth: element.scrollWidth,
    html2canvas: {
      scale: 0.5, // 控制分辨率
      letterRendering: true,
    },
    x: 10,
    y: 10,
  });
};

三、专业方案:Puppeteer(需后端配合)

javascript

// 前端
fetch('/generate-pdf', {
  method: 'POST',
  body: JSON.stringify({ html: document.documentElement.outerHTML }),
  headers: { 'Content-Type': 'application/json' }
});

// Node.js后端
const puppeteer = require('puppeteer');

app.post('/generate-pdf', async (req, res) => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setContent(req.body.html);
  const pdf = await page.pdf({ 
    format: 'A4',
    printBackground: true 
  });
  await browser.close();
  res.setHeader('Content-Type', 'application/pdf');
  res.send(pdf);
});

四、样式优化要点

  1. 打印样式表

css

@media print {
  @page { margin: 0; }
  body { -webkit-print-color-adjust: exact; }
  .no-print { display: none; }
}
  1. 强制分页

css

.page-break {
  page-break-after: always;
  break-after: page;
}
  1. 字体处理

css

@font-face {
  font-family: 'PDFFont';
  src: url('fonts/Songti.ttf') format('truetype');
}

body {
  font-family: 'PDFFont', sans-serif;
}
 

五、方案对比

方案优点缺点
html2canvas+jsPDF纯前端实现,视觉保真度高文字变图片,文件体积较大
jsPDF HTML渲染支持矢量文字,自动分页兼容性问题,复杂布局易出错
Puppeteer完美还原,支持复杂CSS需要后端支持,消耗服务器资源
window.print()零依赖,简单快捷样式控制能力弱,交互体验差

六、常见问题处理

  1. 内容截断

javascript

// 手动计算分页位置
const elementHeight = element.offsetHeight;
const pageHeight = 1122; // A4像素高度(96dpi)
if (elementHeight > pageHeight) {
  // 插入分页符逻辑
}
  1. 图片跨域

html

​
<img crossorigin="anonymous" src="https://example.com/image.jpg">

​

运行 HTML

  1. 提升清晰度

javascript

html2canvas(element, {
  scale: 3, // 最高3倍
  dpi: 300,
  letterRendering: true
});
 

根据具体需求选择方案:需要快速实现选择方案一,注重文字质量选方案二,企业级应用推荐方案三。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2296494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单片机上SPI和IIC的区别

SPI&#xff08;Serial Peripheral Interface&#xff09;和IC&#xff08;Inter-Integrated Circuit&#xff09;是两种常用的嵌入式外设通信协议&#xff0c;它们各有优缺点&#xff0c;适用于不同的场景。以下是它们的详细对比&#xff1a; — 1. 基本概念 SPI&#xff0…

03-DevOps-安装并初始化Gitlab

Gitlab可以理解为是自己搭建的GitHub&#xff0c;也就是自己的代码仓库。 开启macvlan 在192.168.1.10服务器上&#xff0c;构建Macvlan网络&#xff0c;这种网络模式可以为每个容器独立分配ip。 docker network create -d macvlan \--subnet192.168.1.0/24 \--ip-range192.16…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(五)

#作者&#xff1a;闫乾苓 系列前几篇&#xff1a; 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》&#xff1a;link 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;二&#xff09;》&#xff1a; lin…

DFS+回溯+剪枝(深度优先搜索)——搜索算法

DFS也就是深度优先搜索&#xff0c;比如二叉树的前&#xff0c;中&#xff0c;后序遍历都属于DFS。其本质是递归&#xff0c;要学好DFS首先需要掌握递归。接下来咱们就一起来学习DFS涉及的算法。 一、递归 1.什么是递归&#xff1f; 递归可以这样理解把它拆分出来&#xff0…

使用PyCharm创建项目以及如何注释代码

创建好项目后会出现如下图所示的画面&#xff0c;我们可以通过在项目文件夹上点击鼠标右键&#xff0c;选择“New”菜单下的“Python File”来创建一个 Python 文件&#xff0c;在给文件命名时建议使用英文字母和下划线的组合&#xff0c;创建好的 Python 文件会自动打开&#…

Spring MVC 拦截器(Interceptor)与过滤器(Filter)的区别?

1、两者概述 拦截器&#xff08;Interceptor&#xff09;&#xff1a; 只会拦截那些被 Controller 或 RestController 标注的类中的方法处理的请求&#xff0c;也就是那些由 Spring MVC 调度的请求。过滤器&#xff08;Filter&#xff09;&#xff1a; 会拦截所有类型的 HTTP …

【零基础学Mysql】常用函数讲解,提升数据操作效率的利器

以耳倾听世间繁华&#xff0c;以语表达心中所想 大家好,我是whisperrrr. 前言&#xff1a; 大家好&#xff0c;我是你们的朋友whisrrr。在日常工作中&#xff0c;MySQL作为一款广泛使用的开源关系型数据库&#xff0c;其强大的功能为我们提供了便捷的数据存储和管理手段。而在…

防火墙安全综合实验

防火墙安全综合实验 一、拓扑信息 二、需求及配置 实验步骤 需求一&#xff1a;根据下表&#xff0c;完成相关配置 设备接口VLAN接口类型SW2GE0/0/2VLAN 10AccessGE0/0/3VLAN 20AccessGE0/0/1VLAN List&#xff1a;10 20Trunk 1、创建vlan10和vlan20 2、将接口划分到对应…

RabbitMQ 消息顺序性保证

方式一&#xff1a;Consumer设置exclusive 注意条件 作用于basic.consume不支持quorum queue 当同时有A、B两个消费者调用basic.consume方法消费&#xff0c;并将exclusive设置为true时&#xff0c;第二个消费者会抛出异常&#xff1a; com.rabbitmq.client.AlreadyClosedEx…

DeepSeek R1 简单指南:架构、训练、本地部署和硬件要求

DeepSeek R1 简单指南&#xff1a;架构、训练、本地部署和硬件要求 DeepSeek 的 LLM 推理新方法 DeepSeek 推出了一种创新方法&#xff0c;通过强化学习 (RL) 来提高大型语言模型 (LLM) 的推理能力&#xff0c;其最新论文 DeepSeek-R1 对此进行了详细介绍。这项研究代表了我们…

1.攻防世界 unserialize3(wakeup()魔术方法、反序列化工作原理)

进入题目页面如下 直接开审 <?php // 定义一个名为 xctf 的类 class xctf {// 声明一个公共属性 $flag&#xff0c;初始值为字符串 111public $flag 111;// 定义一个魔术方法 __wakeup()// 当对象被反序列化时&#xff0c;__wakeup() 方法会自动调用public function __wa…

【R语言】卡方检验

一、定义 卡方检验是用来检验样本观测次数与理论或总体次数之间差异性的推断性统计方法&#xff0c;其原理是比较观测值与理论值之间的差异。两者之间的差异越小&#xff0c;检验的结果越不容易达到显著水平&#xff1b;反之&#xff0c;检验结果越可能达到显著水平。 二、用…

2025.2.9机器学习笔记:PINN文献阅读

2025.2.9周报 文献阅读题目信息摘要Abstract创新点网络架构实验结论缺点以及后续展望 文献阅读 题目信息 题目&#xff1a; GPT-PINN:Generative Pre-Trained Physics-Informed Neural Networks toward non-intrusive Meta-learning of parametric PDEs期刊&#xff1a; Fini…

JVM(Java 虚拟机)

Java语言的解释性和编译性&#xff08;通过JVM 的执行引擎&#xff09; Java 代码&#xff08;.java 文件&#xff09;要先使用 javac 编译器编译为 .class 文件&#xff08;字节码&#xff09;&#xff0c;紧接着再通过JVM 的执行引擎&#xff08;Execution Engine&#xff09…

利用二分法进行 SQL 盲注

什么是sql注入&#xff1f; SQL 注入&#xff08;SQL Injection&#xff09;是一种常见的 Web 安全漏洞&#xff0c;攻击者可以通过构造恶意 SQL 语句来访问数据库中的敏感信息。在某些情况下&#xff0c;服务器不会直接返回查询结果&#xff0c;而是通过布尔值&#xff08;Tr…

大模型数据集全面整理:444个数据集下载地址

本文针对Datasets for Large Language Models: A Comprehensive Survey 中的 444 个数据集&#xff08;涵盖8种语言类别和32个领域&#xff09;进行完整下载地址整理收集。 2024-02-28&#xff0c;由杨刘、曹家欢、刘崇宇、丁凯、金连文等作者编写&#xff0c;深入探讨了大型语…

Linux 创建进程 fork()、vfork() 与进程管理

Linux 创建进程 fork、vfork、进程管理 一、Linux的0号、1号、2号进程二、Linux的进程标识三、fork() 函数1、基本概念2、函数特点3、用法以及应用场景&#xff08;1&#xff09;父子进程执行不同的代码&#xff08;2&#xff09;进程执行另一个程序 4、工作原理 四、vfork() 函…

2025web寒假作业二

一、整体功能概述 该代码构建了一个简单的后台管理系统界面&#xff0c;主要包含左侧导航栏和右侧内容区域。左侧导航栏有 logo、管理员头像、导航菜单和安全退出按钮&#xff1b;右侧内容区域包括页头、用户信息管理内容&#xff08;含搜索框和用户数据表格&#xff09;以及页…

鸿蒙NEXT API使用指导之文件压缩和邮件创建

鸿蒙NEXT API 使用指导 一、前言二、邮件创建1、拉起垂类应用2、 UIAbilityContext.startAbilityByType 原型2.1、wantParam2.2、abilityStartCallback 与 callback 3、拉起邮箱类应用3.1、单纯拉起邮箱应用3.2、传入带附件的邮件 三、压缩文件1、认识 zlib2、压缩处理2.1、单文…

javaEE-10.CSS入门

目录 一.什么是CSS ​编辑二.语法规则: 三.使用方式 1.行内样式: 2.内部样式: 3.外部样式: 空格规范 : 四.CSS选择器类型 1.标签选择器 2.类选择器 3.ID选择器 4.通配符选择器 5.复合选择器 五.常用的CSS样式 1.color:设置字体颜色 2.font-size:设置字体大小 3…