JavaScript基于pdf-lib实现图片转PDF工具

 更新时间:2026年03月19日 08:40:10   作者:滕青山  
在开发小程序时,实现如图片转 PDF这样的功能时,常常面临一些挑战,为了解决这些痛点,我们探索了一个更优的实现路径:使用JavaScript基于pdf-lib实现图片转PDF工具,需要的朋友可以参考下

这篇只讲功能层 JavaScript 实现。这个工具是我用 Vue 组织页面交互,再配合 pdf-lib 在浏览器端完成 PDF 生成。pdf-lib 是公开可用的通用开源库(MIT 许可),可直接用于浏览器和 Node.js。核心流程是:

上传图片 -> 读取并建模 -> 调整顺序/旋转 -> 计算页面与布局 -> 写入 PDF -> 下载

在线工具网址:https://see-tool.com/image-to-pdf
工具截图:\

0)先说下 pdf-lib 在这个工具里的角色

pdf-lib 在这里主要负责三件事:创建 PDF 文档、把图片字节嵌入文档、按给定坐标绘制到页面。

它的调用模型很直接:

create/load -> addPage/embed -> drawImage -> save

对图片转 PDF 这类场景,它有几个很实用的点:

  • API 稳定,浏览器端可直接运行,不依赖后端转换
  • 页面坐标和尺寸控制细,适合做“边距、缩放、居中、铺满”这类排版
  • 同一套逻辑可扩展到“合并现有 PDF、写文本、填表单”等后续功能

这个工具里最核心的一步是 embedJpg + drawImage:前者把二进制图片数据放进 PDF,后者决定图片在页面中的位置和显示尺寸。只要前面把页面尺寸和布局参数算对,最终导出的版式就会稳定可控。

1)状态模型:围绕“图片队列 + 转换配置”组织

工具初始化后维护一个状态对象,核心字段如下:

  • images:当前待转换图片队列
  • settings:页面尺寸、方向、布局、边距、文件名
  • isConverting:是否正在生成,防止重复点击

每张图片对象都会保存:name、size、format、preview(dataURL)、width、height、rotation。这样后续排序、旋转、预览和生成 PDF 都可以直接复用同一份数据。

2)文件接入:先过滤,再读取 DataURL 和尺寸

上传与拖拽最后都进入同一入口,先只保留图片类型,再做数量上限控制。每个文件会经过两步:

  1. FileReader.readAsDataURL 读成 DataURL
  2. 用 Image 对象加载,拿到 naturalWidth/naturalHeight

核心读取函数:

function readFileAsDataUrl(file) {
  return new Promise(function (resolve, reject) {
    var reader = new FileReader();
    reader.onload = function (event) {
      resolve(
        String(event.target && event.target.result ? event.target.result : ""),
      );
    };
    reader.onerror = function () {
      reject(new Error("file_read_failed"));
    };
    reader.readAsDataURL(file);
  });
}

读取成功后把图片对象追加进队列,页面统计和预览区会立即重渲染。

3)页面尺寸计算:统一转换到 PDF 点单位

PDF 里统一使用 point(pt),因此先做单位换算:

  • MM_TO_PT = 72 / 25.4
  • PX_TO_PT = 72 / 96

固定纸张(A4/A3/Letter/Legal)先由毫米转 pt;如果用户选“原始尺寸”,则按图片像素转 pt。方向为 auto 时,会根据图片宽高自动判定横版或竖版。

function getPageSizePt(pageSize, orientation, imageWidth, imageHeight) {
  var widthPt;
  var heightPt;

  if (pageSize === "original") {
    widthPt = imageWidth * PX_TO_PT;
    heightPt = imageHeight * PX_TO_PT;
  } else {
    var pageSizeValue = PAGE_SIZES_MM[pageSize] || PAGE_SIZES_MM.a4;
    widthPt = pageSizeValue.width * MM_TO_PT;
    heightPt = pageSizeValue.height * MM_TO_PT;
  }

  // 省略方向交换逻辑
  return {
    widthPt: widthPt,
    heightPt: heightPt,
    orientation: finalOrientation,
  };
}

4)布局算法:contain / cover / center

每页先算内容区:

contentWidth = pageWidth - margin * 2

contentHeight = pageHeight - margin * 2

然后按布局模式计算绘制矩形:

  • contain:完整显示图片,按最小缩放比适配
  • cover:铺满内容区,按最大缩放比适配
  • center:不缩放,居中绘制

当边距过大导致内容区宽高小于等于 0 时,直接抛错,转换流程会给出明确提示。

5)旋转处理:先画到 Canvas,再统一转 JPEG

为了让 pdf-lib 写入稳定,工具会先把当前图片(含旋转角度)绘制到 canvas,再导出 JPEG 字节。角度支持 0/90/180/270。

function drawImageToJpeg(image, rotation) {
  var angle = Number(rotation || 0);
  var swapSide = angle === 90 || angle === 270;
  var canvas = document.createElement("canvas");

  canvas.width = swapSide ? image.naturalHeight : image.naturalWidth;
  canvas.height = swapSide ? image.naturalWidth : image.naturalHeight;

  var context = canvas.getContext("2d", { alpha: false });
  context.fillStyle = "#ffffff";
  context.fillRect(0, 0, canvas.width, canvas.height);
  context.translate(canvas.width / 2, canvas.height / 2);
  context.rotate((angle * Math.PI) / 180);
  context.drawImage(image, -image.naturalWidth / 2, -image.naturalHeight / 2);

  return parseDataUrl(canvas.toDataURL("image/jpeg", 0.92));
}

这里先铺白底,可以避免透明图在 PDF 中出现黑底。

6)PDF 生成:逐页 addPage + embedJpg + drawImage

转换时按图片队列顺序循环处理,每张图先算页面尺寸和布局,再写入 PDF:

async function buildPdfBlobWithPdfLib(pages, PDFDocument) {
  var pdfDocument = await PDFDocument.create();

  for (var i = 0; i < pages.length; i += 1) {
    var item = pages[i];
    var page = pdfDocument.addPage([item.pageWidthPt, item.pageHeightPt]);
    var jpg = await pdfDocument.embedJpg(item.imageBytes);
    page.drawImage(jpg, {
      x: item.xPt,
      y: item.yPt,
      width: item.drawWidthPt,
      height: item.drawHeightPt,
    });
  }

  var pdfBytes = await pdfDocument.save();
  return new Blob([pdfBytes], { type: "application/pdf" });
}

最终通过对象 URL 触发浏览器下载,文件名会先做非法字符清洗。

7)交互动作如何影响最终 PDF

工具里“上移/下移/拖拽排序/旋转/删除”都直接改 images 队列,因此最终 PDF 页序与图片列表始终一致。

  • 排序:交换数组元素或拖拽后 splice
  • 旋转:rotation = (rotation + 90) % 360
  • 删除:按 id 过滤

由于生成时是按队列顺序遍历,交互结果会无缝反映到导出文件。

8)转换过程控制:状态锁 + 分阶段提示

点击“转换”后会先进入 isConverting = true:

  • 禁用上传、配置项和操作按钮
  • 显示“正在生成第 X / N 页”
  • 完成后恢复可操作状态

错误按场景给出明确反馈,比如无图片、文件名为空、边距过大、生成失败。整条链路是纯前端闭环:从读取、排版到导出,都在浏览器端完成。

以上就是JavaScript基于pdf-lib实现图片转PDF工具的详细内容,更多关于JavaScript pdf-lib图片转PDF的资料请关注脚本之家其它相关文章!

相关文章

  • 微信小程序实现云开发上传文件、图片功能

    微信小程序实现云开发上传文件、图片功能

    在使用小程序的过程中,在编辑个人资料时,通常会面临上传头像、上传背景图片的情况,而这个开发过程需要怎样实现呢?这篇文章主要给大家介绍了关于微信小程序实现云开发上传文件、图片功能的相关资料,需要的朋友可以参考下
    2022-12-12
  • JS原型、原型链深入理解

    JS原型、原型链深入理解

    这篇文章主要针对javascript中的原型、原型链深入理解,原型是JavaScript中一个比较难理解的概念,本文为大家解决这个难题,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • JavaScript判断用户是否对表单进行了修改的方法

    JavaScript判断用户是否对表单进行了修改的方法

    这篇文章主要介绍了JavaScript判断用户是否对表单进行了修改的方法,实例分析了javascript对表单操作与判定的技巧,需要的朋友可以参考下
    2015-03-03
  • TypeScript内置工具类型快速入门运用

    TypeScript内置工具类型快速入门运用

    TypeScript 中内置了很多工具类型,我们无需导入,可以直接使用。 其中的很多都是比较常用的,接下来我们根据使用范围来一一介绍,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-03-03
  • 基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用

    基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用

    一般Web界面包括的界面控件有:单行文本框、多行文本框、密码文本框、下拉列表Combobox、日期输入控件、数值输入控件、单项选择、复选框、表格控件DataGrid、树形控件、布局控件、弹出式对话框、提示信息、列表控件等,这些界面控件的操作都有哪些不同,下面逐一介绍
    2015-12-12
  • js实现鼠标感应图片展示的方法

    js实现鼠标感应图片展示的方法

    这篇文章主要介绍了js实现鼠标感应图片展示的方法,实例分析了javascript鼠标事件及样式的操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • 小程序input数据双向绑定实现方法

    小程序input数据双向绑定实现方法

    这篇文章主要介绍了小程序input数据双向绑定实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • 微信小程序-详解数据缓存

    微信小程序-详解数据缓存

    每个微信小程序都可以有自己的本地缓存,本篇文章主要介绍了微信小程序-详解数据缓存,可以通过函数可以对本地缓存进行设置、获取和清理,有兴趣的可以了解一下。
    2016-11-11
  • vue中使用cropperjs的方法

    vue中使用cropperjs的方法

    这篇文章给大家总结了在.vue文件里cropperjs的使用方法和经验教训,感兴趣的朋友跟随脚本之家小编学习吧
    2018-03-03
  • js实现定时进度条完成后切换图片

    js实现定时进度条完成后切换图片

    这篇文章主要介绍了js实现定时进度条,进度100%以后可以切换图片,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01

最新评论