最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript基于pdf-lib實(shí)現(xiàn)圖片轉(zhuǎn)PDF工具

 更新時(shí)間:2026年03月19日 08:40:10   作者:滕青山  
在開發(fā)小程序時(shí),實(shí)現(xiàn)如圖片轉(zhuǎn) PDF這樣的功能時(shí),常常面臨一些挑戰(zhàn),為了解決這些痛點(diǎn),我們探索了一個(gè)更優(yōu)的實(shí)現(xiàn)路徑:使用JavaScript基于pdf-lib實(shí)現(xiàn)圖片轉(zhuǎn)PDF工具,需要的朋友可以參考下

這篇只講功能層 JavaScript 實(shí)現(xiàn)。這個(gè)工具是我用 Vue 組織頁面交互,再配合 pdf-lib 在瀏覽器端完成 PDF 生成。pdf-lib 是公開可用的通用開源庫(MIT 許可),可直接用于瀏覽器和 Node.js。核心流程是:

上傳圖片 -> 讀取并建模 -> 調(diào)整順序/旋轉(zhuǎn) -> 計(jì)算頁面與布局 -> 寫入 PDF -> 下載

在線工具網(wǎng)址:https://see-tool.com/image-to-pdf
工具截圖:\

0)先說下 pdf-lib 在這個(gè)工具里的角色

pdf-lib 在這里主要負(fù)責(zé)三件事:創(chuàng)建 PDF 文檔、把圖片字節(jié)嵌入文檔、按給定坐標(biāo)繪制到頁面。

它的調(diào)用模型很直接:

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

對圖片轉(zhuǎn) PDF 這類場景,它有幾個(gè)很實(shí)用的點(diǎn):

  • API 穩(wěn)定,瀏覽器端可直接運(yùn)行,不依賴后端轉(zhuǎn)換
  • 頁面坐標(biāo)和尺寸控制細(xì),適合做“邊距、縮放、居中、鋪滿”這類排版
  • 同一套邏輯可擴(kuò)展到“合并現(xiàn)有 PDF、寫文本、填表單”等后續(xù)功能

這個(gè)工具里最核心的一步是 embedJpg + drawImage:前者把二進(jìn)制圖片數(shù)據(jù)放進(jìn) PDF,后者決定圖片在頁面中的位置和顯示尺寸。只要前面把頁面尺寸和布局參數(shù)算對,最終導(dǎo)出的版式就會穩(wěn)定可控。

1)狀態(tài)模型:圍繞“圖片隊(duì)列 + 轉(zhuǎn)換配置”組織

工具初始化后維護(hù)一個(gè)狀態(tài)對象,核心字段如下:

  • images:當(dāng)前待轉(zhuǎn)換圖片隊(duì)列
  • settings:頁面尺寸、方向、布局、邊距、文件名
  • isConverting:是否正在生成,防止重復(fù)點(diǎn)擊

每張圖片對象都會保存:name、size、format、preview(dataURL)、width、height、rotation。這樣后續(xù)排序、旋轉(zhuǎn)、預(yù)覽和生成 PDF 都可以直接復(fù)用同一份數(shù)據(jù)。

2)文件接入:先過濾,再讀取 DataURL 和尺寸

上傳與拖拽最后都進(jìn)入同一入口,先只保留圖片類型,再做數(shù)量上限控制。每個(gè)文件會經(jīng)過兩步:

  1. FileReader.readAsDataURL 讀成 DataURL
  2. Image 對象加載,拿到 naturalWidth/naturalHeight

核心讀取函數(shù):

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);
  });
}

讀取成功后把圖片對象追加進(jìn)隊(duì)列,頁面統(tǒng)計(jì)和預(yù)覽區(qū)會立即重渲染。

3)頁面尺寸計(jì)算:統(tǒng)一轉(zhuǎn)換到 PDF 點(diǎn)單位

PDF 里統(tǒng)一使用 point(pt),因此先做單位換算:

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

固定紙張(A4/A3/Letter/Legal)先由毫米轉(zhuǎn) pt;如果用戶選“原始尺寸”,則按圖片像素轉(zhuǎn) pt。方向?yàn)?auto 時(shí),會根據(jù)圖片寬高自動判定橫版或豎版。

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

每頁先算內(nèi)容區(qū):

contentWidth = pageWidth - margin * 2

contentHeight = pageHeight - margin * 2

然后按布局模式計(jì)算繪制矩形:

  • contain:完整顯示圖片,按最小縮放比適配
  • cover:鋪滿內(nèi)容區(qū),按最大縮放比適配
  • center:不縮放,居中繪制

當(dāng)邊距過大導(dǎo)致內(nèi)容區(qū)寬高小于等于 0 時(shí),直接拋錯(cuò),轉(zhuǎn)換流程會給出明確提示。

5)旋轉(zhuǎn)處理:先畫到 Canvas,再統(tǒng)一轉(zhuǎn) JPEG

為了讓 pdf-lib 寫入穩(wěn)定,工具會先把當(dāng)前圖片(含旋轉(zhuǎn)角度)繪制到 canvas,再導(dǎo)出 JPEG 字節(jié)。角度支持 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 中出現(xiàn)黑底。

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

轉(zhuǎn)換時(shí)按圖片隊(duì)列順序循環(huán)處理,每張圖先算頁面尺寸和布局,再寫入 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 觸發(fā)瀏覽器下載,文件名會先做非法字符清洗。

7)交互動作如何影響最終 PDF

工具里“上移/下移/拖拽排序/旋轉(zhuǎn)/刪除”都直接改 images 隊(duì)列,因此最終 PDF 頁序與圖片列表始終一致。

  • 排序:交換數(shù)組元素或拖拽后 splice
  • 旋轉(zhuǎn):rotation = (rotation + 90) % 360
  • 刪除:按 id 過濾

由于生成時(shí)是按隊(duì)列順序遍歷,交互結(jié)果會無縫反映到導(dǎo)出文件。

8)轉(zhuǎn)換過程控制:狀態(tài)鎖 + 分階段提示

點(diǎn)擊“轉(zhuǎn)換”后會先進(jìn)入 isConverting = true

  • 禁用上傳、配置項(xiàng)和操作按鈕
  • 顯示“正在生成第 X / N 頁”
  • 完成后恢復(fù)可操作狀態(tài)

錯(cuò)誤按場景給出明確反饋,比如無圖片、文件名為空、邊距過大、生成失敗。整條鏈路是純前端閉環(huán):從讀取、排版到導(dǎo)出,都在瀏覽器端完成。

以上就是JavaScript基于pdf-lib實(shí)現(xiàn)圖片轉(zhuǎn)PDF工具的詳細(xì)內(nèi)容,更多關(guān)于JavaScript pdf-lib圖片轉(zhuǎn)PDF的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 微信小程序?qū)崿F(xiàn)云開發(fā)上傳文件、圖片功能

    微信小程序?qū)崿F(xiàn)云開發(fā)上傳文件、圖片功能

    在使用小程序的過程中,在編輯個(gè)人資料時(shí),通常會面臨上傳頭像、上傳背景圖片的情況,而這個(gè)開發(fā)過程需要怎樣實(shí)現(xiàn)呢?這篇文章主要給大家介紹了關(guān)于微信小程序?qū)崿F(xiàn)云開發(fā)上傳文件、圖片功能的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • JS原型、原型鏈深入理解

    JS原型、原型鏈深入理解

    這篇文章主要針對javascript中的原型、原型鏈深入理解,原型是JavaScript中一個(gè)比較難理解的概念,本文為大家解決這個(gè)難題,感興趣的小伙伴們可以參考一下
    2016-02-02
  • JavaScript判斷用戶是否對表單進(jìn)行了修改的方法

    JavaScript判斷用戶是否對表單進(jìn)行了修改的方法

    這篇文章主要介紹了JavaScript判斷用戶是否對表單進(jìn)行了修改的方法,實(shí)例分析了javascript對表單操作與判定的技巧,需要的朋友可以參考下
    2015-03-03
  • TypeScript內(nèi)置工具類型快速入門運(yùn)用

    TypeScript內(nèi)置工具類型快速入門運(yùn)用

    TypeScript 中內(nèi)置了很多工具類型,我們無需導(dǎo)入,可以直接使用。 其中的很多都是比較常用的,接下來我們根據(jù)使用范圍來一一介紹,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • 基于MVC4+EasyUI的Web開發(fā)框架形成之旅之界面控件的使用

    基于MVC4+EasyUI的Web開發(fā)框架形成之旅之界面控件的使用

    一般Web界面包括的界面控件有:單行文本框、多行文本框、密碼文本框、下拉列表Combobox、日期輸入控件、數(shù)值輸入控件、單項(xiàng)選擇、復(fù)選框、表格控件DataGrid、樹形控件、布局控件、彈出式對話框、提示信息、列表控件等,這些界面控件的操作都有哪些不同,下面逐一介紹
    2015-12-12
  • js實(shí)現(xiàn)鼠標(biāo)感應(yīng)圖片展示的方法

    js實(shí)現(xiàn)鼠標(biāo)感應(yīng)圖片展示的方法

    這篇文章主要介紹了js實(shí)現(xiàn)鼠標(biāo)感應(yīng)圖片展示的方法,實(shí)例分析了javascript鼠標(biāo)事件及樣式的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 小程序input數(shù)據(jù)雙向綁定實(shí)現(xiàn)方法

    小程序input數(shù)據(jù)雙向綁定實(shí)現(xiàn)方法

    這篇文章主要介紹了小程序input數(shù)據(jù)雙向綁定實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • 微信小程序-詳解數(shù)據(jù)緩存

    微信小程序-詳解數(shù)據(jù)緩存

    每個(gè)微信小程序都可以有自己的本地緩存,本篇文章主要介紹了微信小程序-詳解數(shù)據(jù)緩存,可以通過函數(shù)可以對本地緩存進(jìn)行設(shè)置、獲取和清理,有興趣的可以了解一下。
    2016-11-11
  • vue中使用cropperjs的方法

    vue中使用cropperjs的方法

    這篇文章給大家總結(jié)了在.vue文件里cropperjs的使用方法和經(jīng)驗(yàn)教訓(xùn),感興趣的朋友跟隨腳本之家小編學(xué)習(xí)吧
    2018-03-03
  • js實(shí)現(xiàn)定時(shí)進(jìn)度條完成后切換圖片

    js實(shí)現(xiàn)定時(shí)進(jìn)度條完成后切換圖片

    這篇文章主要介紹了js實(shí)現(xiàn)定時(shí)進(jìn)度條,進(jìn)度100%以后可以切換圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01

最新評論

岑溪市| 思南县| 灵璧县| 苏尼特左旗| 汉源县| 咸宁市| 栾城县| 兴山县| 红桥区| 镇平县| 策勒县| 康定县| 封丘县| 斗六市| 嵩明县| 丰台区| 云和县| 格尔木市| 姚安县| 禹城市| 牟定县| 九龙县| 德清县| 巨野县| 枣庄市| 淮滨县| 清水县| 岳普湖县| 黑山县| 清流县| 宿松县| 南丰县| 浙江省| 乐清市| 香河县| 本溪| 九龙城区| 元江| 米泉市| 绵阳市| 西宁市|